Тестовый столбец существует, Добавить столбец и Обновить столбец

Я пытаюсь написать сценарий обновления базы данных 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, прежде чем я смогу обновить столбец. Я пробовал различные перестановки, но я никуда не спешу.


person David Clarke    schedule 03.05.2010    source источник


Ответы (5)


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

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
person Aaronaught    schedule 04.05.2010
comment
Именно то, что я искал, а также. Спасибо тебе за это. - person reaper_unique; 24.02.2014
comment
В более сложном случае вы можете использовать временную таблицу для сохранения данных после оператора GO. просто убедитесь, что вы не забыли очистить его после. - person Jaked222; 19.04.2018

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

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
person Ola Berntsson    schedule 02.02.2018

Попробуйте добавить оператор "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
person mdma    schedule 03.05.2010
comment
Неправильный синтаксис рядом с «0». Это одна из перестановок, которые я уже пробовал. - person David Clarke; 04.05.2010

Хотя принятый ответ работает, в более сложном случае вы можете использовать временную таблицу для сохранения данных после оператора 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
person Jaked222    schedule 20.04.2018

Если вы используете по крайней мере 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. Если столбец уже существует, никакие записи не будут изменены.

person srbrills    schedule 17.05.2019