Обновить параметр 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 из моментального снимка базы данных)
Используя вышеизложенное:
- Мне не пришлось беспокоиться о ограничениях и ключах, поскольку имена таблиц/ограничений всегда остаются неизменными (мне не нужно переименовывать что-либо)
- У меня есть резервная копия моих данных (моментальный снимок), на которые я могу положиться, чтобы проверить, что ничего не пропало.
- Мне не нужно обновлять личность
Я понимаю, что удаление таблицы может быть не всегда простым, если таблица ссылается на другие таблицы. В этом случае это было не так. Мне повезло.