Тестовый столбец существует, Добавить столбец и Обновить столбец - PullRequest
47 голосов
/ 04 мая 2010

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

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 возвращает ошибку «Неверное имя столбца« IsDownloadable »», т. Е. Мне нужно зафиксировать DDL, прежде чем я смогу обновить столбец. Я пробовал разные варианты, но быстро никуда не попал.

Ответы [ 5 ]

79 голосов
/ 04 мая 2010

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

Сценарии 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
1 голос
/ 02 февраля 2018

Меня часто раздражала эта проблема, и, к сожалению, решение, предложенное в ответе Ааронаута , быстро становится грязным, когда речь идет о @parameters и 'strings'. Однако я нашел другой обходной путь, используя синонимы:

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
0 голосов
/ 18 мая 2019

Если вы используете хотя бы SQL Server 2008, вы можете указать WITH VALUES во время добавления столбца, что заполнит существующие записи значением по умолчанию для этого атрибута.

IF COL_LENGTH('[dbo].[Trucks]', 'Is4WheelDrive') IS NULL
BEGIN

    ALTER TABLE [dbo].[Trucks]
    ADD [Is4WheelDrive] BIT NULL DEFAULT 1
    WITH VALUES;

END

Это добавит новый столбец [Is4WheelDrive] к таблице [dbo].[Trucks], если этот столбец не существует. Новый столбец, если он будет добавлен, заполнит существующие записи значением по умолчанию, которое в этом случае является значением BIT 1. Если столбец уже существует, записи не будут изменены.

0 голосов
/ 20 апреля 2018

Хотя принятый ответ работает, в более сложном случае вы можете использовать временную таблицу для сохранения данных после оператора GO. просто не забудьте почистить его после.

Например:

-- Create a tempTable if it doesn't exist. Use a unique name here
IF OBJECT_ID('tempdb..#tempTable') IS NOT NULL DROP TABLE #tempTable
CREATE TABLE #tempTable (ColumnsCreated bit)

-- Create your new column if it doesn't exist. Also, insert into the tempTable.
IF NOT EXISTS (
    SELECT * FROM   INFORMATION_SCHEMA.COLUMNS 
    WHERE  TABLE_NAME = 'targetTable' AND COLUMN_NAME = 'newColumn')
BEGIN
    INSERT INTO #tempTable VALUES (1)

    ALTER TABLE .dbo.targetTable ADD newColumn [SMALLINT] NULL ;
END

GO

-- If the tempTable was inserted into, our new columns were created.
IF (EXISTS(SELECT * FROM #tempTable))
    BEGIN
    -- Do some data seeding or whatever
    END

-- Clean up - delete the tempTable.
IF OBJECT_ID('tempdb..#tempTable') IS NOT NULL DROP TABLE #tempTable
0 голосов
/ 04 мая 2010

Попробуйте добавить оператор "GO" после ALTER TABLE.

Для меня это было новостью, но здесь здесь говорится , что все операторы в пакете (предшествующие GO) скомпилированы в один план запроса.) Без GO в SQL весь план фактически один запрос.

РЕДАКТИРОВАТЬ: Так как 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
...