【发布时间】:2012-08-19 06:47:02
【问题描述】:
我正在尝试使用高性能表参数方法 (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 返回的值与插入原始记录的顺序不同。因此,我无法以编程方式为每条记录分配从OUTPUT 语句返回的item_id 值。
似乎唯一有效的解决方案是 SELECT 返回我刚刚插入的整个记录列表,但坦率地说,我更喜欢任何可以减少通过 SQL Server 网卡传输的数据量的解决方案。有没有人在检索身份值的同时对大型插入有更好的解决方案?
编辑:让我尝试进一步澄清这个问题。问题是我希望我的 C# 程序了解 SQL Server 分配给我刚刚插入的数据的标识值。顺序不是必需的;但我希望能够在 C# 中获取任意记录集,使用快速表参数方法插入它们,然后在 C# 中分配它们自动生成的 ID 号,而无需将整个表重新查询回内存。
鉴于这是一个人工测试集,我试图将其压缩为尽可能小的可读代码。让我描述一下我用来解决这个问题的方法:
- 在我的原始代码中,在此示例来自的应用程序中,我将使用 1500 万条单独的插入语句插入大约 1500 万行,并在每次插入后检索标识值。这行得通,但速度很慢。
- 我修改了代码,使用高性能表参数进行插入。然后我将在 C# 中处理所有对象,并从数据库中读回整个对象。但是,原始记录有几十列,其中包含大量 varchar 和十进制值,因此这种方法虽然速度快且有效,但网络流量非常密集。
- 我现在开始研究是否可以使用表参数插入,同时要求 SQL Server 只报告标识值。我尝试了
scope_identity()和OUTPUT,但到目前为止都没有成功。
基本上,如果 SQL Server 始终按照我提供的顺序插入记录,这个问题就会得到解决。是否可以让 SQL Server 插入记录完全按照表值参数插入中提供的顺序?
EDIT2:这种方法似乎与 Cade Roux 下面引用的非常相似:
但是,在文章中,作者使用了一个神奇的唯一值“ProductNumber”,将插入的信息从“输出”值连接到原始表值参数。如果我的表没有神奇的唯一值,我正在尝试弄清楚如何做到这一点。
【问题讨论】:
-
您在表上有什么类型的索引.. 有没有办法执行存储过程来获取 MaxId 表示最后更新行的标识列..?我希望我能正确理解你的问题..?基本上返回@@IDENTITY
-
我最初用 scope_identity() 尝试过这个 - 结果也是不可预测的。在 100k 行中,似乎会以错误的顺序插入 10,000 行的随机片段。
-
@DJKRAZE - 整个表定义在上面的 SQL 脚本中;我正是使用该脚本在空白数据库上创建了该表。如果您想自己测试,请随意尝试:)
-
marc_s 感谢您的澄清..
标签: c# sql-server performance