【问题标题】:Performance of updating one big table using values from one small table使用一张小表的值更新一张大表的性能
【发布时间】:2011-01-01 21:33:57
【问题描述】:

首先,我知道使用来自table_b 的值更新table_a 的sql 语句的形式为:

甲骨文:

UPDATE table_a 
  SET (col1, col2) = (SELECT cola, colb 
                        FROM table_b 
                       WHERE table_a.key = table_b.key) 
WHERE EXISTS (SELECT * 
                FROM table_b 
               WHERE table_a.key = table_b.key)

MySQL:

UPDATE table_a 
INNER JOIN table_b ON table_a.key = table_b.key 
SET table_a.col1 = table_b.cola, 
    table_a.col2 = table_b.colb

据我了解,数据库引擎将遍历table_a 中的记录,并使用table_b 中匹配记录的值更新它们。

所以,如果我在 table_a 中有 1000 万条记录,而在 table_b 中只有 10 条记录:

  1. 这是否意味着引擎将通过 table_a 进行 1000 万次迭代以更新 10 条记录? Oracle/MySQL/etc 是否足够聪明,只能通过 table_b 进行 10 次迭代?

  2. 有没有办法强制引擎实际遍历table_b 中的记录而不是table_a 来进行更新? sql语句有替代语法吗?

假设 table_a.keytable_b.key 已编入索引。

【问题讨论】:

    标签: sql mysql performance oracle


    【解决方案1】:

    任何一个引擎都应该足够聪明,可以根据表 b 中只有 10 行的事实来优化查询。引擎如何确定要做什么取决于索引和统计信息等因素。

    如果“key”列是主键和/或已编入索引,则引擎只需做很少的工作即可运行此查询。它基本上已经“知道”匹配行的位置,并很快查找它们。它根本不需要“迭代”。

    如果键列上没有索引,引擎将不得不进行“表扫描”(大致相当于“迭代”)来找到正确的值并匹配它们。这意味着它必须扫描 1000 万行。

    阅读一下所谓的执行计划。这基本上是对引擎必须执行哪些工作才能运行您的查询的解释(有些数据库仅以文本形式显示,有些可以选择以图形方式查看)。学习如何解释执行计划将使您深入了解向表中添加索引和优化查询。

    如果它们不起作用(已经有一段时间了),请查看它们,但它类似于:

    • 在 MySQL 中,将工作“EXPLAIN”放在 SELECT 语句的前面
    • 在 Oracle 中,在运行 SELECT 语句之前运行“SET AUTOTRACE ON”

    我认为使用 JOIN 而不是 WHERE EXISTS 编写第一个 (Oracle) 查询会更好。引擎可能足够聪明,可以以任何一种方式正确优化它。一旦掌握了解释执行计划的窍门,您就可以双向运行它并亲自查看。 :)

    【讨论】:

    • 实际上,在 Oracle 中,IN/EXISTS 比使用 JOIN 更好(给定适当的标准):explainextended.com/2009/09/30/in-vs-join-vs-exists-oracle
    • 有趣。 HASH JOIN SEMI 是一个巧妙的优化。尽管只有列没有被索引似乎才重要。我可能仍然会使用 JOIN 失败(更具表现力的恕我直言)。但在某些情况下绝对有用!
    【解决方案2】:

    好吧,我知道回答自己的问题通常是不受欢迎的,但我已经接受了另一个答案并且不会不接受它,所以这里是 ..

    我发现了一个更好的替代方案,我想与遇到相同情况的任何人分享:MERGE 声明。

    显然,较新的 Oracle 版本引入了这个 MERGE 声明,这简直就是打击!不仅在大多数情况下性能要好得多,而且语法如此简单和有意义,以至于我觉得使用 UPDATE 语句很愚蠢!来了..

    MERGE INTO table_a
    USING table_b
    ON (table_a.key = table_b.key)
    WHEN MATCHED THEN UPDATE SET
      table_a.col1 = table_b.cola,
      table_a.col2 = table_b.colb;
    

    更重要的是,当table_a 没有与table_b 中的某些记录匹配的记录时,我还可以扩展语句以包含INSERT 操作:

    MERGE INTO table_a
    USING table_b
    ON (table_a.key = table_b.key)
    WHEN MATCHED THEN UPDATE SET
      table_a.col1 = table_b.cola,
      table_a.col2 = table_b.colb
    WHEN NOT MATCHED THEN INSERT
      (key, col1, col2)
      VALUES (table_b.key, table_b.cola, table_b.colb);
    

    这种新的语句类型在我发现它的那一天让我很开心:)

    【讨论】:

      猜你喜欢
      • 2013-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 2020-01-19
      • 2018-07-09
      • 2022-01-10
      相关资源
      最近更新 更多