2010-05-03 2 views
41

Я пытаюсь написать сценарий обновления базы данных SQL Server. Я хочу проверить наличие столбца в таблице, а затем, если он не существует, добавьте столбец со значением по умолчанию и, наконец, обновите этот столбец на основе текущего значения другого столбца в той же таблице. Я хочу, чтобы этот скрипт выполнялся несколько раз, при первом обновлении таблицы и последующих запусках сценарий должен быть проигнорирован. Мой сценарий в настоящее время выглядит следующим образом:Test Column существует, Добавить столбец и Обновить столбец

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable') 
BEGIN 

ALTER TABLE [dbo].[PurchaseOrder] ADD [IsDownloadable] bit NOT NULL DEFAULT 0 

UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref] IS NOT NULL 

END 

SQL Server возвращает ошибку «Invalid имя столбца" IsDownloadable», то есть мне нужно совершить DDL, прежде чем я могу обновить колонку. Я пробовал разные перестановки, но я быстро никуда не уходил.

ответ

75

Этот сценарий не будет успешно работать, если столбец уже не существует, что точно когда вы не нужно.

Сценарии SQL должны быть проанализированы до того, как они будут выполнены. Если столбец не существует в момент разбора скрипта, то синтаксический анализ завершится неудачей. Не имеет значения, что ваши сценарии впоследствии создают столбец; синтаксический анализатор не знает этого.

Если вы хотите получить доступ к столбцу, который вы только что добавили, вам нужно добавить оператор GO (разделитель партии). Однако, как только вы это сделаете, вы больше не сможете поддерживать какой-либо поток управления или переменные из предыдущей партии - это похоже на выполнение двух отдельных скриптов. Это делает сложным одновременно и DDL, и DML, в то же время.

Самый простой обходной путь, который я бы, наверное, порекомендовать для вас, потому что ваш DML не очень сложен, чтобы использовать динамический SQL, который анализатор не будет пытаться не разобрать до «выполнения»:

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable') 
BEGIN 

    ALTER TABLE [dbo].[PurchaseOrder] ADD 
     [IsDownloadable] bit NOT NULL DEFAULT 0 

    EXEC sp_executesql 
     N'UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref] IS NOT NULL' 

END 
+0

Что-то, спасибо. –

+3

Полное объяснение, спасибо – Vladimirs

+1

Точно то, что я искал. Спасибо тебе за это. –

-1

Попробуйте добавить инструкцию «GO» после ALTER TABLE.

Это была новость для меня, но он говорит here, что все утверждения в пакете (те, которые предшествуют GO) скомпилированы в один план запроса.) Не имея GO в SQL, весь план является фактически одним запросом.

EDIT: С GO дает синтаксическую ошибку (которая, казалось мне странным), я создал нечто подобное, и нашел, что это работало

declare @doUpdate bit; 

SELECT @doUpdate = 0; 

IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'PurchaseOrder' AND COLUMN_NAME = 'IsDownloadable') 
BEGIN 
SELECT @doUpdate=1 
END 

IF @doUpdate<>0 
    ALTER TABLE [dbo].[PurchaseOrder] ADD [IsDownloadable] bit NOT NULL DEFAULT 0 

IF @doUpdate<>0 
    UPDATE [dbo].[PurchaseOrder] SET [IsDownloadable] = 1 WHERE [Ref]=0 

COMMIT TRAN 
+0

Неправильный синтаксис около '0'. Это одна из перестановок, которые я уже пробовал. –

1

Я часто раздражался этой проблемой, и, к сожалению, решение, предложенное в Aaronaught's answer, быстро становится беспорядочным, когда задействуются @параметры и «строки». Тем не менее, я нашел другое обходное решение, используя использование синонимов:

IF(COL_LENGTH('MyTable', 'NewCol') IS NULL) 
BEGIN 
    ALTER TABLE MyTable ADD NewCol VARCHAR(16) NULL; 

    CREATE SYNONYM hack FOR MyTable; 
    UPDATE hack SET NewCol = 'Hello ' + OldCol; 
    DROP SYNONYM hack; 

    ALTER TABLE MyTable ALTER COLUMN NewCol VARCHAR(16) NOT NULL; 
END