Я писал (казалось бы) простой фрагмент кода SQL, который удаляет столбец после того, как удостоверится, что столбец существует.
Проблема: если столбец НЕ существует, код внутри в предложении IF сообщает, что не может найти столбец! Что ж, дох, поэтому он внутри предложения IF!
Итак, мой вопрос: почему фрагмент кода, который не следует выполнять, дает ошибки?
Вот фрагмент:
IF exists (select * from syscolumns
WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
ALTER TABLE [dbo].[Table_MD]
DROP COLUMN timeout
END
GO
... и вот ошибка:
Error executing SQL script [...]. Invalid column name 'timeout'
Я использую Microsoft SQL Server 2005 Express Edition.


Возможно, он никогда не будет выполнен, но он будет проанализирован на предмет достоверности сервером Sql. Единственный способ "обойти" это - создать блок динамического sql и затем выборочно выполнить его.
IF exists (select * from syscolumns
WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
DECLARE @SQL nvarchar(1000)
SET @SQL = N'ALTER TABLE [dbo].[Table_MD] DROP COLUMN timeout'
EXEC sp_executesql @SQL
END
GO
Причина: Когда сервер Sql компилирует код, он проверяет его на наличие используемых объектов (если они существуют). Эта процедура проверки игнорирует любые конструкции «IF», «WHILE» и т. д. И просто проверяет все используемые объекты в коде.
Чтобы уточнить, если временная таблица существует, столбцы будут проверяться при компиляции sproc. Если sproc фактически создает таблицу (например, с помощью select), вам может потребоваться удалить таблицу перед перекомпиляцией sproc.
Вот как я заставил это работать:
Внутри предложения IF я изменил команду ALTER ... DROP ... на exec ('ALTER ... DROP ...')
Кажется, что SQL-сервер проверяет достоверность кода при его синтаксическом анализе и видит, что где-то есть ссылка на несуществующий столбец (даже если этот фрагмент кода никогда не будет выполнен).
Использование команды exec(ute) превращает проблемный код в строку, синтаксический анализатор не жалуется, и код выполняется только при необходимости.
Вот измененный фрагмент:
IF exists (select * from syscolumns
WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
exec ('ALTER TABLE [dbo].[Table_MD] DROP COLUMN timeout')
END
GO
Между прочим, в Oracle есть аналогичная проблема и аналогичный обходной путь с использованием предложения «выполнить немедленно».
Правильный. Вы также получите эту ошибку, если есть временные таблицы, которые использует sproc.