更新一个包含数百万条记录的表,已经 4 天了

Luc*_*cky 12 sql-server-2008 sql-server

我目前正在更新一个包含数百万条记录的表,已经 4 天了,查询仍在执行。

我检查了活动监视器,它显示查询正在运行。

在事件日志中根本没有错误。

性能明智:

  • 磁盘 A 中的 Tempdb(850 GB 可用空间)
  • 磁盘 B 中的数据库文件(750 GB 可用空间)
  • 16 GB 内存

请建议我该怎么办?

查询

UPDATE
    dbo.table1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
    dbo.table2 t2
WHERE
    LEFT(dbo.test1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 
Run Code Online (Sandbox Code Playgroud)

gbn*_*gbn 8

此查询要求您扫描表中的每一行,因为

  • 我猜 procodet 或 ProviderCode 未编入索引
  • 即使它们被索引,你也有一个 LEFT,它是 WHERE 谓词上的一个函数
  • 而且你也有 COLLATE,它实际上是 WHERE 谓词上的一个函数

“WHERE 谓词上的函数”意味着不会使用索引

如果你批处理它(比如在 UPDATE TOP (10000) ... AND costPercentage IS NULL)那么你需要一个关于 costPercentage 的索引这假设你正在设置它。

我看到的唯一解决方案是

  • 批量填充新表,例如,基于主键
  • 创建索引的计算列以隐藏 LEFT 和 COLLATE 表达式,然后运行更新

  • 鉴于查询将列的 3 个字符部分连接到另一列的 3 个字符部分,结果很可能包含重复项。这比仅仅更新数百万行要糟糕得多。我敢打赌它正在扫描数十亿的工作表。 (3认同)

usr*_*usr 3

这个查询有一个有趣的细节,我一开始没有发现。感谢 Fabricio Araujo 的回答,我现在明白了:您正在访问两个表。我以前从未见过更新语句的这种用法,我不建议使用它。我建议您根据 Fabricio 的答案使用更直观的连接语法。

可能的原因是两个表之间的联接产生了极端数量的行。如果LEFT(col, 3)表达式产生重复值,则可能会发生这种情况。如果它产生 10 个重复项,这将导致连接结果中有 100000x100000=10000000000 行。

我不认为索引在这里发挥作用。SQL Server 可以使用散列或合并联接来很好地解决此未索引联接。不需要4天。

另一个可能的原因是低估了连接输入或输出的基数。SQL Server 可能选择了循环连接。

由于这仍然是猜测,我建议您发布查询计划,这将阐明这个问题。