我们的服务器应用程序每天以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
...每个传入的行都有自己的'OR tag = n'或'OR longTag ='x''子句。
然后我使用ISQLXMLBulkLoad执行XML批量加载,一次加载所有新记录。
巨大的DELETE声明有时会超时,需要30秒或更长时间。我不确定为什么。
当记录从套接字进入时,必须插入它们或者它们必须替换现有的行。我这样做是最好的方式吗?
编辑:新行与替换行的比率将非常倾向于新行。在我看到的生产数据中,每次修正通常会有100-1000个新行。
编辑2 :插入和删除都必须作为单个事务处理。如果插入或删除失败,它们必须都回滚,使表格处于插入和插入之前的状态。删除开始了。
编辑3 :关于NULL标签。我需要先简要介绍一下系统。这是一个交易系统的数据库。 MyTable是一个包含两种交易的交易表:所谓的“日间交易”和所谓的“开仓”。日间交易只是交易 - 如果您是期权交易者并且您进行了交易,那么该交易将是该系统中的日间交易。开盘头基本上是您的投资组合的摘要,直到今天。开仓头寸和日内交易都存储在同一个表格中。日间交易有标签(longTags或数字标签),而开仓位则没有。打开位置可能有重复的行 - 这很好&正常。但是,日交易不能有重复的行。如果日间交易与数据库中已存在的某个记录具有相同的标记,则表中的数据将替换为新数据。
因此tag和amp;中的值有4种可能性。 longTag:
1)标签非零& longTag为空:这是一个带有数字标识符的日间交易。 2)tag为零,longTag具有非空字符值。这是一个带有字母数字标识符的日间交易。 3)tag为零,longTag为空:这是一个开放位置。 4)标签非零,longTag具有非空字符值。这可以防止我们的服务器软件发生的每一件事,但如果它发生了,那么longTag将被忽略,它将被视为与案例#1相同。同样,这不会发生。
答案 0 :(得分:5)
我认为将巨大的DELETE语句拆分为2 DELETE可能有所帮助。
1 DELETE处理标记和单独的DELETE来处理longTag。 这将有助于SQL服务器有效地选择使用索引。
当然,您仍然可以在1个DB往返中触发2个DELETE语句。
希望这有帮助
答案 1 :(得分: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;
看看我们在这做了什么?由于我们仅在标记上加入,因此标记索引的使用可能性更大。
首先我们删除了所有新内容,然后我们插入了新的替代品。我们也可以在这里做更新。哪个更快取决于您的表结构及其索引。
我们没有编写脚本来完成它,我们只需要在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;
最后,我们清除了u_real_table:
delete from u_real_table;
显然,我们将每个删除/插入对包装在一个事务中,这样只有在后续插入成功时才删除,然后我们将整个事件包装在另一个事务中。因为它是一个合乎逻辑的工作单元。
此方法可减少您的手动操作,减少手动错误的可能性,并且有可能加快删除速度。
请注意,这依赖于缺少的标签和longTags正确为空,而不是零或空字符串。
答案 2 :(得分:3)
也许:
DELETE FROM MyRecords
WHERE tag IN (1, 2, 555) -- build a list
OR longTag IN ('LongTag1')
我怀疑索引会帮助你删除但是会大大减慢你的插入,所以我不会那么玩。但是后来我的直觉还不是很完美,你可能能够调整FillFactor或其他项目以解决这个问题,而且我确实知道你确实想要描述它们的一件事。
另一种选择是将新插入加载到临时表(名为InputQueue
),然后加入MyRecords上的临时表来处理过滤更新。这也可以通过两个步骤轻松完成更新:您可以将Tags和longTags删除为单独的操作,这可能会更有效。
答案 3 :(得分:3)
这样的事情可以简化流程(你只需要插入行,无论它们是否已经存在 - 不需要前面的DELETE语句):
CREATE TRIGGER dbo.TR_MyTable_Merge
ON dbo.MyTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION
DELETE MyTable
FROM MyTable t INNER JOIN inserted i ON t.tag = i.tag
DELETE MyTable
FROM MyTable t INNER JOIN inserted i ON t.longTag = i.longTag
INSERT MyTable
SELECT * FROM inserted
COMMIT TRANSACTION
SET NOCOUNT OFF;
END
编辑:以前将DELETE语句组合成两个单独的语句,以实现最佳索引使用。
根本不使用DELETE,而是在索引上更容易更新受影响/重复的行。
答案 4 :(得分:3)
观看此视频,了解如何进行“啃咬”删除。这个过程运作良好,绝对可以减少您所看到的锁定/碰撞问题:
答案 5 :(得分:2)
似乎您的表未在(tag)
和(longTag)
构建两个索引:一个在(tag)
,一个在(longTag)
如果您打算删除大量的记录,那么声明两个表变量,用值填充它们并删除如下:
DECLARE @tag TABLE (id INT);
DECLARE @longTag TABLE (id VARCHAR(50));
INSERT
INTO @tag
VALUES (`tag1`)
INSERT
INTO @tag
VALUES (`tag2`)
/* ... */
INSERT INTO @longTag
VALUES ('LongTag1')
/* ... */
DELETE
FROM MyRecords r
WHERE r.tag IN (SELECT * FROM @tag)
OR r.longTag IN (SELECT * FROM @longTag)
您也可以尝试执行两遍DELETE
:
DELETE
FROM MyRecords r
WHERE r.tag IN (SELECT * FROM @tag)
DELETE
FROM MyRecords r
WHERE r.longTag IN (SELECT * FROM @longTag)
并查看哪些语句运行时间更长,以查看索引是否存在问题。
答案 6 :(得分:2)
使用OR可能会导致表扫描 - 您可以将其分解为四个语句吗?在交易中包装每一个也可以加快速度。
DELETE from MyRecords
WHERE tag = 1
DELETE from MyRecords
WHERE tag = 2
DELETE from MyRecords
WHERE tag = 555
DELETE from MyRecords
WHERE longTag = 'LongTag1'
答案 7 :(得分:1)
<强>索引:强>
考虑为longTag使用索引持久计算列,该列存储longTag的校验和。您可以索引一个4字节的int值(86939596),而不是索引'LongTag1'。
然后你的查找[希望*]更快,你只需要在查询/删除中包含longTag值。您的代码会稍微复杂一些,但索引可能会更有效率。
*需要测试