更新现有表中的ANSI_NULLS选项

时间:2012-04-04 11:19:50

标签: sql sql-server-2008 clustered-index ansi-nulls

在我们的数据库中,有一个用ANSI_NULLS OFF创建的表。现在我们使用此表创建了一个视图。我们想为此视图添加聚簇索引。

创建聚簇索引时,它显示的错误就像无法创建索引一样,因为此特定表的ANSI_NULL已关闭。

此表包含大量数据。所以我想将此选项更改为ON而不会丢失任何数据。

有没有办法改变表来修改这个选项。请提出你的建议。

3 个答案:

答案 0 :(得分:16)

这是cross posted on Database Administrators所以我也可以在这里发布我的答案,以帮助未来的搜索者。

可以使用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将重置标识列的种子,如果您对标识没有UNIQUE或PRIMARY KEY约束(例如,在SQL 2014中使用CLUSTERED COLUMNSTORE索引时),您将不会立即注意到它。 您需要使用DBCC CHECKIDENT('dbo.YourTable',RESEED,[reseed value])来再次正确设置种子值。

答案 1 :(得分: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,您需要DROPCREATE他们。{/ p>

答案 2 :(得分:0)

我尝试了上面推荐的SWITCH选项,但无法重置身份。我找不到原因。

我使用了以下替代方法:

  1. 为包含表格的数据库创建数据库快照
  2. 您要更新的表的脚本表定义
  3. 删除要更新的表(确保已成功创建数据库快照)
  4. 从步骤2获取的脚本中将SET ANSI NULL从OFF更新为ON并运行更新的脚本。表现在已重新创建。
  5. 将数据库快照中的数据填充到您的表中: 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
  6. 手动迁移非聚集索引(从数据库快照中获取脚本)
  7. 使用上述内容:

    • 我不必担心约束和键,因为表/约束名称总是保持不变(我不需要重命名)
    • 我有一个我的数据备份(快照),我可以依赖它来仔细检查没有遗漏任何内容。
    • 我不需要重新设置身份

    我意识到如果在其他表中引用表,则删除表可能并不总是直截了当。在这种情况下,我的情况并非如此。我很幸运。