Почему блок T-SQL выдает ошибку, даже если он даже не должен выполняться? - PullRequest
5 голосов
/ 23 сентября 2008

Я писал (казалось бы) прямой фрагмент 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.

Ответы [ 4 ]

10 голосов
/ 23 сентября 2008
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» и т. Д. И просто проверяет все используемые объекты в коде.

0 голосов
/ 23 сентября 2008

Кстати, в Oracle есть похожая проблема и аналогичный обходной путь, использующий предложение «немедленное выполнение».

0 голосов
/ 23 сентября 2008

Вот как я заставил это работать:

Внутри предложения 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
0 голосов
/ 23 сентября 2008

Возможно, оно никогда не будет выполнено, но Sql Server анализирует его на достоверность. Единственный способ «обойти» это построить блок динамического SQL и затем выборочно выполнить его

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...