我正在尝试使用Dapper支持我的服务器应用程序的数据访问。
我的服务器应用程序有另一个应用程序,它以每分钟400的速度将记录删除到我的数据库中。
我的应用程序将它们分批拉出,处理它们,然后从数据库中删除它们。
由于数据在我处理过程中继续流入数据库,因此我无法说出delete from myTable where allProcessed = true
。
但是,我确实知道要删除的行的PK值。所以我想做一个delete from myTable where Id in @listToDelete
问题是,如果我的服务器连续下降了6分钟,那么我有超过2100行要删除。
由于Dapper接受我的@listToDelete并将每个变为参数,我对delete的调用失败。 (导致我的数据清除进一步落后。)
在Dapper处理这个问题的最佳方法是什么?
注意: 我查看了Tabled Valued Parameters,但从我所看到的情况来看,它们并不是performant。这段我的建筑是我系统的瓶颈,我需要非常非常快。
答案 0 :(得分:14)
一种选择是在服务器上创建临时表,然后使用批量加载工具将所有ID一次上传到该表中。然后使用join,EXISTS或IN子句仅删除上传到临时表中的记录。
批量加载是SQL Server中经过充分优化的路径,它应该非常快。
例如:
CREATE TABLE #RowsToDelete(ID INT PRIMARY KEY)
#RowsToDelete
DELETE FROM myTable where Id IN (SELECT ID FROM #RowsToDelete)
DROP TABLE #RowsToDelte
(如果您关闭会话,表格也将自动删除)(假设Dapper)代码示例:
conn.Open();
var columnName = "ID";
conn.Execute(string.Format("CREATE TABLE #{0}s({0} INT PRIMARY KEY)", columnName));
using (var bulkCopy = new SqlBulkCopy(conn))
{
bulkCopy.BatchSize = ids.Count;
bulkCopy.DestinationTableName = string.Format("#{0}s", columnName);
var table = new DataTable();
table.Columns.Add(columnName, typeof (int));
bulkCopy.ColumnMappings.Add(columnName, columnName);
foreach (var id in ids)
{
table.Rows.Add(id);
}
bulkCopy.WriteToServer(table);
}
//or do other things with your table instead of deleting here
conn.Execute(string.Format(@"DELETE FROM myTable where Id IN
(SELECT {0} FROM #{0}s", columnName));
conn.Execute(string.Format("DROP TABLE #{0}s", columnName));
答案 1 :(得分:5)
为了让这段代码正常工作,我走到了黑暗的一边。
由于Dapper使我的列表成为参数。而SQL Server无法处理很多参数。 (我以前从未需要双位数参数)。我不得不使用Dynamic SQL。
所以这是我的解决方案:
string listOfIdsJoined = "("+String.Join(",", listOfIds.ToArray())+")";
connection.Execute("delete from myTable where Id in " + listOfIdsJoined);
在每个人抓住他们的火把和干草叉之前,让我解释一下。
我知道构建动态SQL是糟糕的juju,但在这种情况下,我只是看不出它会如何导致安全风险。
答案 2 :(得分:2)
Dapper请求具有参数的对象列表作为属性,因此在上面的情况下,具有Id作为属性的对象列表将起作用。
connection.Execute("delete from myTable where Id in (@Id)", listOfIds.AsEnumerable().Select(i=> new { Id = i }).ToList());
这样可行。