许多行的最高效(快速)T-SQL DELETE?

Joh*_*ing 5 t-sql sql-server performance

我们的服务器应用程序每天以1000-2000行的速率接收有关要添加到数据库的行的信息.表中有两个互斥的列,用于唯一标识一行:一个是名为"tag"的数字标识符,另一个是名为"longTag"的50字符串.一行可以有标签或longTag; 不是都.

从套接字进入的每一行可能已经存在,也可能不存在于表中.如果存在,则必须使用新信息更新该行.如果它不存在,则必须添加.我们使用SQL 2005,在少数情况下甚至使用SQL 2000,因此我们无法使用新的MERGE关键字.

我现在这样做的方法是构建一个巨大的DELETE语句,如下所示:

DELETE from MyRecords
WHERE tag = 1
OR tag = 2
OR longTag = 'LongTag1'
OR tag = 555
Run Code Online (Sandbox Code Playgroud)

...每个传入的行都有自己的'OR tag = n'或'OR longTag ='x''子句.

然后,我使用ISQLXMLBulkLoad执行XML批量加载,一次加载所有新记录.

巨大的DELETE语句有时会超时,需要30秒或更长时间.我不知道为什么.

当记录从套接字进入时,它们必须插入或者必须替换现有的行.我这样做是最好的方式吗?

编辑:新行与替换行的比率将非常倾向于新行.在我看到的生产数据中,每次修正通常会有100-1000个新行.

编辑2:插入和删除都必须作为单个事务处理.如果插入或删除失败,则必须同时回滚它们,使表处于插入和删除开始之前的状态.

编辑3:关于NULL标签.我需要先简要介绍一下系统.这是一个交易系统的数据库.MyTable是一个包含两种交易的交易表:所谓的"日间交易"和所谓的"开仓".日间交易只是交易 - 如果您是期权交易者并且您进行了交易,那么该交易将是该系统中的日间交易.开盘头基本上是您的投资组合的摘要,直到今天.开仓和日内交易都存储在同一个表中.日间交易有标签(longTags或数字标签),而开仓位则没有.开仓时可能有重复的行 - 这很好且正常.但是,日交易不能有重复的行.如果日间交易与数据库中已存在的某个记录具有相同的标记,则表中的数据将替换为新数据.

因此tag和longTag中的值有4种可能性:

1)tag为非零且longTag为空:这是一个带有数字标识符的日间交易.2)tag为零,longTag具有非空字符值.这是一个带有字母数字标识符的日间交易.3)tag为零,longTag为空:这是一个开放位置.4)标签非零,longTag具有非空字符值.这可以防止我们的服务器软件发生的每一件事,但如果它发生了,那么longTag将被忽略,它将被视为与案例#1相同.同样,这不会发生.

小智 5

我认为将巨大的DELETE语句拆分为2 DELETE可能会有所帮助.

1 DELETE处理标记和单独的DELETE来处理longTag.这将有助于SQL服务器选择有效地使用索引.

当然,您仍然可以在1 DB往返中触发2个DELETE语句.

希望这可以帮助


tpd*_*pdi 4

OR(或 in)几乎就像每个 OR 操作数都是不同的查询一样。也就是说,它变成了表扫描,对于每一行,数据库必须将每个 OR 操作数作为谓词进行测试,直到找到匹配项或用完操作数。

将其打包的唯一原因是使其成为一个逻辑工作单元。您还可以将一堆删除包装在一个事务中,并且仅在所有删除成功完成时才提交。

Quassnoi 提出了一个有趣的建议——使用表格——但由于他随后使用了 IN 和 OR,所以结果是一样的。

但试试这个。

创建一个反映真实表的新表。称之为 u_real_table。在 tag 和 longTag 上索引它。

将所有传入数据放入 u_real_table 中。

现在,当您准备好做大量事情时,请将镜像表加入到标签上的真实表中。从真实表中,删除 u_real_table 中所有标记的行:

delete real_table from real_table a 
   join u_real_table b on (a.tag = b.tag);
insert into real_table select * 
   from u_real_table where tag is not null;
Run Code Online (Sandbox Code Playgroud)

看看我们在这里做了什么?由于我们仅加入 tag,因此可以使用标签索引的可能性更大。

首先我们删除所有新内容,然后插入新的替换内容。我们也可以在这里进行更新。哪个更快取决于您的表结构及其索引。

我们不必编写脚本来完成此操作,只需将记录插入到 u_real_table 中即可。

现在我们对 longTags 做同样的事情:

delete real_table from real_table a 
   join u_real_table b on (a.longTag = b.longTag);
insert into real_table select * 
   from u_real_table where longTag is not null;
Run Code Online (Sandbox Code Playgroud)

最后,我们清除u_real_table:

delete from u_real_table;
Run Code Online (Sandbox Code Playgroud)

显然,我们将整个每个删除/插入对包装在一个事务中,以便只有当后续插入成功时删除才变为真实,然后我们将整个事情包装在另一个事务中。因为它是一个逻辑工作单元。

此方法减少了您的手动工作,减少了手动错误的可能性,并且有可能加快删除速度。

请注意,这依赖于缺失标签和 longTags 正确为空,而不是零或空字符串。