Недопустимое имя столбца SQL Server

Если я попытаюсь выполнить следующий код, я получу ошибки

Сообщение 207, уровень 16, состояние 1, строка 3 Недопустимое имя столбца «Другой». Сообщение 207, уровень 16, состояние 1, строка 4 Недопустимое имя столбца «Другой».

несмотря на то, что предикат для обоих утверждений IF всегда оценивается как ложный.

CREATE TABLE #Foo (Bar INT)
GO
IF (1=0)
BEGIN
    SELECT Another FROM #Foo
END
GO
IF (1=0)
BEGIN
    ALTER TABLE #Foo ADD Another INT
    SELECT Another FROM #Foo 
END
GO
DROP TABLE #Foo

Это, вероятно, слишком упрощено для примера; на самом деле мне нужно выбрать значения из столбца, но только если столбец существует. Если его нет, то меня это не волнует. В проблеме, из-за которой я задал этот вопрос, мой предикат был похож на EXISTS (SELECT * FROM sys.columns WHERE object_id = @ID AND name = @Name). Есть ли способ добиться этого, не прибегая к моему заклятому врагу Dynamic SQL? Я понимаю, что мой SQL всегда должен быть корректным (т. е. соответствовать грамматике) — даже внутри блока, который никогда не выполняется, — но я ошеломлен тем, что меня также заставляют делать его семантически правильным!

РЕДАКТИРОВАТЬ: Хотя я не уверен, что приведенный ниже код значительно расширяет приведенный выше код, это еще один пример проблемы. В этом сценарии я хочу только установить значение Определенно (которое определенно существует в виде столбца) со значением из Возможно (которое, возможно, существует в виде столбца), если Возможно существует.

IF EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('dbo.TableName', 'U') AND name = 'Maybe')
BEGIN
    UPDATE dbo.TableName SET Definitely = Maybe
END

person Jono    schedule 03.08.2011    source источник
comment
Вы пытались обернуть каждый шаг в хранимую процедуру? Насколько мне известно, некоторые проверки не выполняются до момента выполнения. Так, например, SP1 создает таблицу, SP2 добавляет столбец, а SP3 выбирает из этого столбца.   -  person MatBailie    schedule 03.08.2011


Ответы (5)


SQL Server не выполняется построчно. Он не процедурный, как код .net или Java. Так что нет "неисполненного блока"

Пакет собирается за один раз. На данный момент столбец не существует, но он знает, что таблица будет. В таблице нет столбца с названием «Другой». Потерпеть неудачу.

Как и ожидалось.

Теперь, какова реальная проблема, которую вы пытаетесь решить?

Некоторые варианты:

  • 2 таблицы или одна таблица с обоими столбцами
  • используйте хранимые процедуры, чтобы отделить область действия
  • не используйте временные таблицы (возможно, это не нужно; это может быть ваше процедурное мышление...)
  • динамический SQL (из удаленного ответа Митча)

Изменить после комментария;

Почему бы не скрыть изменения схемы за представлением, а не менять весь код для работы со столбцами, которых может и не быть?

person gbn    schedule 03.08.2011
comment
Люди не мыслят процедурно, мы — нейронная сеть, что, безусловно, ближе к уменьшению карты. Нет, подождите, вы не это имели в виду, я просто устал и теряю шарик. Я сейчас замолчу. - person MatBailie; 03.08.2011
comment
Реальная проблема: таблица с несколькими десятками миллионов строк использует логическое поле (B). Со временем требования изменились, был добавлен более широкий столбец (I), а значения были преобразованы из B -> I. B может быть опущено или не опущено, но ожидается, что SQL справится с обоими случаями. Я не использую временные таблицы, а объем данных отвлекает от хранения промежуточной таблицы. Хранимая процедура будет работать... - person Jono; 03.08.2011
comment
@Jono: вы предоставляете представление, которое скрывает изменения таблицы (например, с I на B), и весь код думает, что это B. Затем вы меняете код по крупицам. Вы не меняете весь свой код для работы с одним, может быть, двумя столбцами... - person gbn; 03.08.2011
comment
@gbm - Интересное предложение, и, возможно, сложность того стоит в правильной ситуации. Спасибо! - person Jono; 03.08.2011
comment
На самом деле proc не будет работать, потому что proc будет разрешать столбцы во время синтаксического анализа. Отложенное разрешение имен работает, только если отсутствует весь объект. Я знаю, что это не идеально, но как насчет select * и работы с выводом на уровне приложения? - person Code Magician; 04.08.2011

Вы можете использовать EXEC, чтобы справиться с этим. Это не совсем динамический SQL, если код никогда не меняется.

Например:

CREATE TABLE dbo.Test (definitely INT NOT NULL)
INSERT INTO dbo.Test (definitely) VALUES (1), (2), (3)

IF EXISTS (SELECT *
           FROM sys.columns
           WHERE object_id = OBJECT_ID('dbo.Test', 'U') AND
                 name = 'Maybe')
BEGIN
    EXEC('UPDATE dbo.Test SET definitely = maybe')
END

SELECT * FROM dbo.Test

ALTER TABLE dbo.Test ADD maybe INT NOT NULL DEFAULT 999

IF EXISTS (SELECT *
           FROM sys.columns
           WHERE object_id = OBJECT_ID('dbo.Test', 'U') AND
                 name = 'Maybe')
BEGIN
    EXEC('UPDATE dbo.Test SET definitely = maybe')
END

SELECT * FROM dbo.Test

DROP TABLE dbo.Test
person Tom H    schedule 03.08.2011
comment
Спасибо за предложение. Я не пробовал EXEC(), но предполагаю, что он ведет себя аналогично EXEC sp_executesql N''. И вы правы, это не динамический SQL как таковой, он просто повторно использует аналогичную технику. - person Jono; 03.08.2011
comment
Отличный ответ. Это полностью помогло нам получить исправление для базы данных на основе условной логики. Ответ gbn хорошо объясняет поведение, но на самом деле он не помогает, ваш помогает. Благодарность! - person neoscribe; 22.11.2012

Вы также можете попробовать ">"Обходной путь" Мартина Смита с использованием несуществующей таблицы для получения "отложенного разрешения имен" для столбцов.

person MichelZ    schedule 19.12.2012
comment
Умный трюк! Задействуя в запросе динамически создаваемую таблицу, компилятор не пытается заранее оценить такой оператор. Для меня это был ответ на вопрос. - person miroxlav; 05.04.2016

Я была такая же проблема. Мы создаем сценарий для всех изменений в течение многих лет, и это первый раз, когда у нас возникает эта проблема. Я пробовал все ваши ответы и не нашел проблему.

В моем случае это произошло из-за временной таблицы в скрипте, который я использую также в хранимой процедуре, хотя каждое предложение прошло. Я обнаружил, что если я добавляю, если существует с падением во временную таблицу после того, как сценарий использует временную таблицу, он работает правильно.

С уважением, Чен

person Chen    schedule 08.12.2013

Получено из ответа @gbn. Что я сделал, чтобы решить эту проблему, так это использовать «GO» между запросом ALTER и запросом, который использует столбец, добавленный ALTER. Это заставит 2 запроса выполняться как отдельные пакеты, тем самым гарантируя, что ваш столбец «Другой» находится перед запросом SELECT.

person chibyk    schedule 10.07.2019