Невалидно име на колона на SQL Server

Ако се опитам да изпълня следния код, получавам грешките

Msg 207, ниво 16, състояние 1, ред 3 Невалидно име на колона „Друго“. Msg 207, ниво 16, състояние 1, ред 4 Невалидно име на колона „Друго“.

въпреки че предикатът и за двата израза IF винаги се оценява като false.

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 винаги трябва да бъде добре оформен (т.е. да отговаря на граматиката) - дори в рамките на блок, който никога не се изпълнява - но съм изумен, че също съм принуден да го направя семантично правилен!

РЕДАКТИРАНЕ: Въпреки че не съм сигурен, че кодът по-долу добавя много към кода по-горе, това е още един пример за проблема. В този сценарий искам само да задам стойността на Definitely (която определено съществува като колона) със стойността от Maybe (която може би съществува като колона), ако Maybe съществува.

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. След това променяте кода малко по малко. Не променяте целия си код, за да работи с една може би 2 колони... - person gbn; 03.08.2011
comment
@gbm - Интересно предложение и може би сложността може да си струва в правилната ситуация. Благодаря! - person Jono; 03.08.2011
comment
Всъщност процедурата няма да работи, защото процедурата ще разреши колоните по време на анализ. Отложеното разрешаване на имена работи само ако целият обект липсва. Знам, че не е идеално, но какво ще кажете за 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

Имах същия проблем. Ние създаваме скрипт за всички промени от години и това е първият път, когато имаме този проблем. Опитах всичките ви отговори и не открих проблема.

В моя случай това беше поради временна таблица в скрипта, която използвам и в съхранена процедура, въпреки че всяко изречение е изчезнало. Открих, че ако добавям if exists с drop към временната таблица, след като скриптът използва временната таблица, той работи правилно.

С най-добри пожелания, Чен

person Chen    schedule 08.12.2013

Извлечено от отговора на @gbn. Това, което направих, за да реша проблема, беше да използвам „GO“ между заявката ALTER и заявката, която използва колоната, добавена от ALTER. Това ще накара 2-те заявки да се изпълняват като отделни партиди, като по този начин се гарантира, че вашата колона „Друга“ е там преди заявката SELECT.

person chibyk    schedule 10.07.2019