Обновить параметр ANSI_NULLS в существующей таблице

В нашей базе данных есть таблица, созданная с помощью ANSI_NULLS OFF. Теперь мы создали представление, используя эту таблицу. И мы хотим добавить кластерный индекс для этого представления.

При создании кластерного индекса отображается ошибка, например, не удается создать индекс, поскольку ANSI_NULL отключен для этой конкретной таблицы.

В этой таблице содержится большой объем данных. Поэтому я хочу изменить этот параметр на ON без потери данных.

Есть ли способ изменить таблицу, чтобы изменить эту опцию. Пожалуйста, дайте свои предложения.

Ответ 1

Это было cross, размещенное на администраторах баз данных, поэтому я мог бы также опубликовать свой ответ отсюда здесь, чтобы помочь будущим искателям.

Это может быть сделано как изменение только метаданных (т.е. без переноса всех данных в новую таблицу) с помощью ALTER TABLE ... SWITCH.

Пример кода ниже

/*Create table with option off*/ 
SET ANSI_NULLS OFF; 

CREATE TABLE dbo.YourTable (X INT) 

/*Add some data*/ 
INSERT INTO dbo.YourTable VALUES (1),(2),(3) 

/*Confirm the bit is set to 0*/ 
SELECT uses_ansi_nulls, * 
FROM   sys.tables 
WHERE  object_id = object_id('dbo.YourTable') 

GO 

BEGIN TRY 
    BEGIN TRANSACTION; 
    /*Create new table with identical structure but option on*/
    SET ANSI_NULLS ON; 
    CREATE TABLE dbo.YourTableNew (X INT) 

    /*Metadata only switch*/
    ALTER TABLE dbo.YourTable  SWITCH TO dbo.YourTableNew;

    DROP TABLE dbo.YourTable; 

    EXECUTE sp_rename N'dbo.YourTableNew', N'YourTable','OBJECT'; 

    /*Confirm the bit is set to 1*/ 
    SELECT uses_ansi_nulls, * 
    FROM   sys.tables 
    WHERE  object_id = object_id('dbo.YourTable') 

    /*Data still there!*/ 
    SELECT * 
    FROM dbo.YourTable

    COMMIT TRANSACTION; 
END TRY 

BEGIN CATCH 
    IF XACT_STATE() <> 0 
      ROLLBACK TRANSACTION; 

    PRINT ERROR_MESSAGE(); 
END CATCH; 

ПРЕДУПРЕЖДЕНИЕ: если в таблице содержится столбец IDENTITY, вам необходимо повторно установить значение IDENTITY. SWITCH TO будет reset семени столбца идентификации, и если у вас нет ограничения UNIQUE или PRIMARY KEY для идентификации (например, при использовании индекса CLUSTERED COLUMNSTORE в SQL 2014), вы не заметите его сразу. Вам необходимо использовать DBCC CHECKIDENT ( "dbo.YourTable", RESEED, [reseed value]), чтобы снова правильно установить начальное значение.

Ответ 2

К сожалению, нет способа сделать это без воссоздания. Вам нужно создать новую таблицу с ANSI_NULLS ON и скопировать туда все данные.

Это должно быть что-то вроде:

SET ANSI_NULLS ON;

CREATE TABLE new_MyTBL (
....
)

-- stop all processes changing your data at this point

SET IDENTITY_INSERT new_MyTBL ON

INSERT new_MyTBL (...)   -- including IDENTITY field 
SELECT ...               -- including IDENTITY field 
FROM MyTBL 

SET IDENTITY_INSERT new_MyTBL OFF

-- alter/drop WITH SCHEMABINDING objects at this point

EXEC sp_rename @objname = 'MyTBL', @newname = 'old_MyTBL'
EXEC sp_rename @objname = 'new_MyTBL', @newname = 'MyTBL'

-- alter/create WITH SCHEMABINDING objects at this point
-- re-enable your processes

DROP TABLE old_MyTBL      -- do that when you are sure that system works OK

Если есть какие-либо зависимые объекты, они будут работать с новой таблицей, как только вы ее переименуете. Но если некоторые из них WITH SCHEMABINDING, вам нужно DROP и CREATE их вручную.

Ответ 3

Я попробовал вариант SWITCH, рекомендованный выше, но не смог ИССЛЕДОВАТЬ идентификатор. Я не мог понять, почему.

Вместо этого я использовал следующий альтернативный подход:

  • Создать моментальный снимок базы данных для базы данных, содержащей таблицу
  • Script таблица определения таблицы, которую вы собираетесь обновить
  • Удалить таблицу, которую вы собираетесь обновить (убедитесь, что моментальный снимок базы данных успешно создан)
  • Обновите SET ANSI NULL с OFF до ON из script, полученного с шага 2, и запустите обновленный script. Теперь таблица воссоздана.
  • Заполнение данных из моментального снимка базы данных в таблице: SET IDENTITY_INSERT TABLE_NAME ON INSERT INTO TABLE_NAME (PK, col1, etc.) SELECT PK, col1, etc. FROM [Database_Snapshot].dbo.TABLE_NAME SET IDENTITY_INSERT TABLE_NAME OFF
  • Перенос некластеризованного индекса вручную (получить script из моментального снимка базы данных)

Используя вышеизложенное:

  • Мне не пришлось беспокоиться о ограничениях и ключах, поскольку имена таблиц/ограничений всегда остаются неизменными (мне не нужно переименовывать что-либо)
  • У меня есть резервная копия моих данных (моментальный снимок), на которые я могу положиться, чтобы проверить, что ничего не пропало.
  • Мне не нужно обновлять личность

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