我正在尝试使用高性能表参数方法(http://www.altdevblogaday.com/2012/05/16/sql-server-high-performance-inserts/)插入记录,我很好奇它是否是可以检索我插入的每条记录的标识值。
目前,答案似乎是否定的——我插入数据,然后检索身份值,但它们不匹配。具体来说,它们大约有 75% 的时间不匹配,并且它们不会以不可预知的方式匹配。这是一些复制此问题的代码:
// Create a datatable with 100k rows
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("item_id", typeof(int)));
dt.Columns.Add(new DataColumn("comment", typeof(string)));
for (int i = 0; i < 100000; i++) {
dt.Rows.Add(new object[] { 0, i.ToString() });
}
// Insert these records and retrieve back the identity
using (SqlConnection conn = new SqlConnection("Data Source=localhost;Initial Catalog=testdb;Integrated Security=True")) {
conn.Open();
using (SqlCommand cmd = new SqlCommand("proc_bulk_insert_test", conn)) {
cmd.CommandType = CommandType.StoredProcedure;
// Adding a "structured" parameter allows you to insert tons of data with low overhead
SqlParameter param = new SqlParameter("@mytable", SqlDbType.Structured);
param.Value = dt;
cmd.Parameters.Add(param);
SqlDataReader dr = cmd.ExecuteReader();
// Set all the records' identity values
int i = 0;
while (dr.Read()) {
dt.Rows[i].ItemArray = new object[] { dr.GetInt32(0), dt.Rows[i].ItemArray[1] };
i++;
}
dr.Close();
}
// Do all the records' ID numbers match what I received back from the database?
using (SqlCommand cmd = new SqlCommand("SELECT * FROM bulk_insert_test WHERE item_id >= @base_identity ORDER BY item_id ASC", conn)) {
cmd.Parameters.AddWithValue("@base_identity", (int)dt.Rows[0].ItemArray[0]);
SqlDataReader dr = cmd.ExecuteReader();
DataTable dtresult = new DataTable();
dtresult.Load(dr);
}
}
使用此 SQL 服务器脚本定义数据库:
CREATE TABLE bulk_insert_test (
item_id int IDENTITY (1, 1) NOT NULL PRIMARY KEY,
comment varchar(20)
)
GO
CREATE TYPE bulk_insert_table_type AS TABLE ( item_id int, comment varchar(20) )
GO
CREATE PROCEDURE proc_bulk_insert_test
@mytable bulk_insert_table_type READONLY
AS
DECLARE @TableOfIdentities TABLE (IdentValue INT)
INSERT INTO bulk_insert_test (comment)
OUTPUT Inserted.item_id INTO @TableOfIdentities(IdentValue)
SELECT comment FROM @mytable
SELECT * FROM @TableOfIdentities
这就是问题所在:从返回的值proc_bulk_insert_test
与插入原始记录的顺序不同。因此,我不能以编程方式为每条记录分配item_id
我从OUTPUT
语句中收到的值。
似乎唯一有效的解决方案是SELECT
支持我刚刚插入的整个记录列表,但坦率地说,我更喜欢任何能够减少通过 SQL Server 网卡传输的数据量的解决方案。有没有人在检索身份值的同时对大型插入有更好的解决方案?
编辑:让我尝试进一步澄清这个问题。问题是我希望我的 C# 程序了解 SQL Server 分配给我刚刚插入的数据的标识值。顺序不是必需的;但我希望能够在 C# 中获取任意记录集,使用快速表参数方法插入它们,然后在 C# 中分配它们自动生成的 ID 号,而无需将整个表重新查询回内存。
鉴于这是一个人工测试集,我试图将其压缩成尽可能小的可读代码。让我描述一下我用来解决这个问题的方法:
- 在我的原始代码中,在这个示例来自的应用程序中,我将使用 1500 万条单独的插入语句插入大约 1500 万行,并在每次插入后取回标识值。这工作但很慢。
- 我使用高性能表参数修改了代码进行插入。然后我将在 C# 中处理所有对象,并从数据库中读回整个对象。然而,原始记录有几十列,其中包含大量的 varchar 和十进制值,因此这种方法非常耗费网络流量,尽管它速度快且有效。
- 我现在开始研究以确定是否可以使用表参数插入,同时要求 SQL Server 只报告标识值。我尝试过
scope_identity()
,OUTPUT
但到目前为止都没有成功。
基本上,如果 SQL Server 总是按照我提供的顺序插入记录,这个问题就会得到解决。是否可以按照表值参数插入中提供的顺序使 SQL 服务器插入记录?
EDIT2:这种方法似乎与 Cade Roux 在下面引用的非常相似:
但是,在文章中,作者使用了一个神奇的唯一值“ProductNumber”,将插入的信息从“输出”值连接到原始表值参数。如果我的表没有神奇的独特价值,我正在尝试弄清楚如何做到这一点。