Ted*_*nce 2 c# sql-server performance
我正在尝试使用高性能表参数方法(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);
}
}
Run Code Online (Sandbox Code Playgroud)
使用此SQL Server脚本定义数据库:
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
Run Code Online (Sandbox Code Playgroud)
这是问题所在:返回的值proc_bulk_insert_test与插入原始记录的顺序不同.因此,我无法以编程方式为每条记录分配item_id从OUTPUT语句中收到的值.
似乎唯一有效的解决方案是SELECT支持我刚刚插入的整个记录列表,但坦率地说,我更喜欢任何可以减少通过SQL Server网卡传输的数据量的解决方案.有没有人在检索身份值时有更好的大插入解决方案?
编辑:让我尝试更多地澄清这个问题.问题是,我希望我的C#程序能够了解SQL Server为刚插入的数据分配的标识值.订单不是必不可少的; 但我希望能够在C#中获取一组任意记录,使用快速表参数方法插入它们,然后在C#中分配它们自动生成的ID号,而不必将整个表重新查询到内存中.
鉴于这是一个人工测试集,我试图将其压缩为尽可能小的可读代码.让我描述一下我用来解决这个问题的方法:
scope_identity()和OUTPUT,但都没有成功迄今在任.基本上,如果SQL Server总是按照我提供的顺序插入记录,这个问题就解决了.是否可以使SQL服务器按照表值参数插入中提供的顺序插入记录?
EDIT2:这种方法与Cade Roux引用的方法非常相似:
但是,在本文中,作者使用魔术唯一值"ProductNumber"将插入的信息从"输出"值连接到原始表值参数.如果我的表没有神奇的唯一值,我正在试图弄清楚如何做到这一点.
你的TVP是一个无序的设置,就像一张普通的桌子.它只在您指定时才有订单.你不仅没有办法在这里指出实际的顺序,你也只是在最后做一个没有ORDER BY的SELECT*.你在这里期待什么顺序?你有效地告诉SQL Server,你不在乎.也就是说,我实现了你的代码,没有问题以正确的顺序返回行.我稍微修改了这个过程,以便您可以实际告诉哪个标识值属于哪个注释:
DECLARE @TableOfIdentities TABLE (IdentValue INT, comment varchar(20))
INSERT INTO bulk_insert_test (comment)
OUTPUT Inserted.item_id, Inserted.comment
INTO @TableOfIdentities(IdentValue, comment)
SELECT comment FROM @mytable
SELECT * FROM @TableOfIdentities
Run Code Online (Sandbox Code Playgroud)
然后我使用这个代码调用它(我们不需要所有的C#):
DECLARE @t bulk_insert_table_type;
INSERT @t VALUES(5,'foo'),(2,'bar'),(3,'zzz');
SELECT * FROM @t;
EXEC dbo.proc_bulk_insert_test @t;
Run Code Online (Sandbox Code Playgroud)
结果:
1 foo
2 bar
3 zzz
Run Code Online (Sandbox Code Playgroud)
如果您想确保输出符合身份分配的顺序(不一定与您的无序TVP具有相同的"顺序"),您可以添加ORDER BY item_id到过程中的最后一个选择.
如果要插入到目标表中,以便您的标识值按照对您很重要的顺序,那么您有几个选项:
向TVP添加一列并将订单插入该列,然后使用游标按顺序迭代行,并一次插入一行.恕我直言,比调用每一行的整个程序更有效率.
在TVP中添加一个表示顺序的列,并在插入上使用ORDER BY.这不是保证,但相对可靠,特别是如果您使用MAXDOP 1消除并行问题.
无论如何,您似乎在ORDER上有很多相关性.你的订单究竟意味着什么?如果您想在订单上添加一些含义,则不应使用IDENTITY列.
| 归档时间: |
|
| 查看次数: |
1720 次 |
| 最近记录: |