【问题标题】:SQL update with sub-query takes too long to run带有子查询的 SQL 更新运行时间过长
【发布时间】:2015-11-25 23:38:40
【问题描述】:

我有一个 SQL 更新查询,它使用我的测试数据运行,但没有完成(2 小时或更长时间)我的生产数据。

查询的目的

我有一个使用代码字符串而不是 ID 的 ADDRESSES 表。因此,例如 ADDRESSES.COUNTRY_CODE = "USA" 而不是 3152。为了参照完整性,我将这些代码字符串更改为代码 ID。

架构

地址(约 356,000 条记录)

  • ADDR_ID (PK)
  • COUNTRY_CODE (varchar)
  • 地址行 1 (varchar)

COUNTRY_CODES

  • CODE_ID (PK)
  • CODE_STRING (varchar)
  • 等。

步骤

首先,我创建一个临时表来存储具有适当代码 ID 的地址记录:

CREATE TABLE ADDRESS_TEMP
AS
   SELECT ADDR_ID, CODE_ID
     FROM    ADDRESSES
          LEFT JOIN
             COUNTRY_CODES
          ON ADDRESSES.COUNTRY_CODE = CODE_STRING

其次,我将 COUNTRY_CODE 列清空并将其类型更改为 NUMBER。

第三,我将 COUNTRY_CODE 列设置为代码 ID:

UPDATE ADDRESSES
   SET COUNTRY_CODE =
          (SELECT ADDRESS_TEMP.CODE_ID
             FROM ADDRESS_TEMP
            WHERE ADDRESS_TEMP.ADDR_ID = ADDRESSES.ADDR_ID)

这第三步需要几个小时才能完成(2 小时并且还在继续)。 ADDRESSES 表有约 356,000 条记录。没有错误;它仍在运行。

问题

为什么这个更新查询没有完成?是不是效率非常低?我想我可以看到子查询可能是 N2 算法,但我对 SQL 缺乏经验。

【问题讨论】:

  • 我认为更新查询会影响表中的每一行,因为它没有 where 子句。您是否尝试过运行 SQL Profiler 来查看实际传递给数据库的内容?
  • 我明白你的意思,@SimonPrice,但是这个查询确实成功地完成了预期的结果,数据集要小得多。我不完全确定 update-set-subquery 是如何工作的(我几个月前写了这个查询),所以我将重新访问文档以刷新我的记忆。
  • 尝试在 ADDR_ID 列的 ADDRESS_TEMP 中添加索引。
  • 如果要完成一个小得多的数据集,那么您正在使用的数据集有多大?

标签: sql oracle performance subquery


【解决方案1】:

只是对 Oracle 优化器内部的猜测:“ADDRESS_TEMP”在 addr_id 上没有主键,也没有任何索引。因此,更新确实采用了 n^2 的方法。它基本上通过临时表扫描地址表中的每一行。

所以,建议:将 addr_id 设为临时表的主键。

如果这没有帮助我们提供 Oracle 优化器生成的执行树。这将使幕后实际发生的事情更加清晰。

【讨论】:

  • 将 ADDRESS_TEMP.ADDR_ID 设为主键后,查询需要 3.5 分钟才能运行。这是我对指数重要性的第一次真实体验——令人大开眼界。
  • @DavidS 我很惊讶查询不会在地址和 address_temp 之间使用散列连接,并且想知道 address_temp 表上是否没有统计信息,这会扭曲执行计划。基于散列连接的更新应该比基于连接所有行的嵌套循环的更新快。您必须查看前后的解释计划才能确定,但​​请不要从中学到“索引 = 好,全表扫描 = 坏”——这是一个粗略的简化,特别是对于批量数据操作这种类型的。
  • 大部分内容都超出了我的想象,@DavidAldridge,因为我只知道基本的 SQL,但感谢您警告我不要草率下结论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 2021-09-21
  • 1970-01-01
相关资源
最近更新 更多