SET IDENTITY_INSERT 从 SQL Server 2000 到 2005 的行为有何不同?
Posted
技术标签:
【中文标题】SET IDENTITY_INSERT 从 SQL Server 2000 到 2005 的行为有何不同?【英文标题】:Difference in behavior for SET IDENTITY_INSERT from SQL Server 2000 to 2005? 【发布时间】:2009-09-08 21:11:13 【问题描述】:我有一个应用程序(当前)需要使用 SQL Server 2000 和 SQL Server 2005 提供的数据库。
我正在尝试以编程方式将数据库从一台服务器移动到另一台服务器。我使用的所有表都有一个自动递增的主键。由于这些自动增量键在其他表中用作外键,因此我复制表的攻击计划是暂时关闭目标表上的自动增量,逐字插入记录(使用原始主键),然后打开自动-重新增加。
如果我理解正确,我可以通过执行一次“打开/关闭自动增量”一个表
SET IDENTITY_INSERT tbl_Test ON;
...
SET IDENTITY_INSERT tbl_Test OFF;
我的实现是在 VB 2005 中,使用 System.Data.SqlClient。
为简洁起见,我将只展示与构建 SQL 查询相关的代码行。
创建表:
Dim createTableStatement As String = _
"CREATE TABLE tbl_Test (" & _
"ID_Test INTEGER PRIMARY KEY IDENTITY," & _
"TestData INTEGER" & _
")"
Dim createTableCommand As New SqlCommand(createTableStatement, connection)
createTableCommand.ExecuteNonQuery()
打开或关闭 IDENTITY_INSERT:
Dim turnOffIdentInsertStatement As String = _
"SET IDENTITY_INSERT tbl_Test OFF;"
Dim turnOffIdentInsertCommand As New SqlCommand(turnOffIdentInsert, connection)
turnOffIdentInsertCommand.ExecuteNonQuery()
Dim turnOnIdentInsertStatement As String = _
"SET IDENTITY_INSERT tbl_Test ON;"
Dim turnOnIdentInsertCommand As New SqlCommand(turnOnIdentInsert, connection)
turnOnIdentInsertCommand.ExecuteNonQuery()
正常插入新记录,不指定主键:
Dim insertRegularStatement As String = _
"INSERT INTO tbl_Test (TestData) VALUES (42);"
Dim insertRegularCommand As New SqlCommand(insertRegularStatement, connection)
insertRegularCommand.ExecuteNonQuery()
使用显式主键插入新记录:
Dim insertWithIDStatement As String = _
"INSERT INTO tbl_Test (ID_Test, TestData) VALUES (20, 42);"
Dim insertWithIDCommand As New SqlCommand(insertWithIDStatement, connection)
insertWithIDCommand.ExecuteNonQuery()
所以,我的计划是调用打开 IDENTITY_INSERT 的代码,使用显式主键添加记录,然后关闭 IDENTITY_INSERT。
当我与 SQL Server 2000 交谈时,此方法似乎工作正常,但当我尝试对 SQL Server 2005 完全相同的代码时遇到异常:
"Cannot insert explicit value for identity column in table 'tbl_Test' when IDENTITY_INSERT is set to OFF."
有什么提示吗?一如既往地提前感谢。
【问题讨论】:
【参考方案1】:SqlBulkCopy 值得一看,因为它在导入批量数据方面进行了优化。
根据具体情况,还值得研究的是使用 SSIS 任务批量复制数据(也使用批量复制 API)或使用 BULK INSERT 语句链接服务器(同样,批量复制 API)。
至于设置身份,set identity_insert
语句是在与插入相同的连接上执行的吗?由于 SQL Server 2005 仅在当前连接上保留该设置。
因为以下对我来说在 SQL Server 2008 Express 上运行良好:
using (var conn = CreateConnection())
conn.Open();
new SqlCommand("SET IDENTITY_INSERT Foo ON", conn).ExecuteNonQuery();
new SqlCommand("INSERT INTO Foo(id) VALUES (1)", conn).ExecuteNonQuery();
new SqlCommand("SET IDENTITY_INSERT Foo OFF", conn).ExecuteNonQuery();
【讨论】:
你找到了。我正在打开/关闭命令之间的连接。也感谢您将我指向 SqlBulkCopy! 啊……这会不会是偶然与隐式交易有关? 大卫,可能。不过,我还不够熟悉,无法确定。 SET IDENTITY_INSERT 不关心事务,它不参与其中。然而,它确实关心连接,它只为当前连接设置。我还没有测试范围,所以我不确定。【参考方案2】:您可能需要在 SET IDENTITY ... ON
语句之后插入 GO 语句。
【讨论】:
感谢您抽出宝贵时间回复大卫!【参考方案3】:在 SQL 查询分析器会话中,我会在 IDENTITY_INSERT 之后和实际插入之前发出 GO。也许这也适用于在代码中调用它?
【讨论】:
感谢您抽出宝贵时间回复!非常感谢。以上是关于SET IDENTITY_INSERT 从 SQL Server 2000 到 2005 的行为有何不同?的主要内容,如果未能解决你的问题,请参考以下文章
SET IDENTITY_INSERT (Transact-SQL) 允许将显式值插入到表的标识列中
SET IDENTITY_INSERT OFF 的数据库范围等效项
SQL Server 错误:“无法为标识列插入显式值”,即使我 SET IDENTITY_INSERT ON