【问题标题】:SQL optimization: deletes taking a long timeSQL 优化:删除耗时较长
【发布时间】:2010-03-16 15:58:28
【问题描述】:

我有一个 Oracle SQL 查询作为存储过程的一部分:

DELETE FROM item i 
 WHERE NOT EXISTS (SELECT 1 FROM item_queue q WHERE q.n=i.n) 
 AND NOT EXISTS (SELECT 1 FROM tool_queue t WHERE t.n=i.n);

关于表格的一些信息:

  • item 包含大约 10k 行,索引在 n 列上
  • item_queue 包含大约 100 万行,并且索引在 n 列上
  • tool_queue 还包含大约 500 万行索引

我想知道是否可以以某种方式优化查询/子查询以使它们运行得更快,我认为删除通常相当快

【问题讨论】:

  • 请重新格式化您的代码,使其不在滚动窗格中。

标签: sql oracle optimization stored-procedures plsql


【解决方案1】:

把你的delete变成select,然后就可以检查优化查询部分了。

否则请注意 - 删除并不是最快的事情。删除时会发生很多事情。

OTOH 我认真地认为....问题出在两个子查询上。查询计划是什么样的?

【讨论】:

    【解决方案2】:

    尝试类似:

     DELETE FROM item WHERE n NOT IN 
         (SELECT i.n FROM item i INNER JOIN item_queue q ON i.n = q.n
          UNION SELECT i.n FROM item i INNER JOIN tool_queue t ON i.n = t.n)
    

    在您的示例中,您的相关子查询每个都运行 10K 次。此技术将运行两个 INNER JOIN 查询以获取要删除的“n”列表。

    您可能需要稍微修改一下 SQL;我不熟悉 Oracle 方言。

    【讨论】:

    • Oracle 的语法应该没问题,但我认为您需要使用or 而不是and,因为原始检查两者都不存在。此外,使用 excplicit joins 会使其更具可读性。
    • 谢谢彼得,你是对的。我编辑了答案以分离出两个查询并将结果集 UNION 在一起,从而避免了进入混乱的多表 JOIN 的需要;我重新格式化以使用 INNER JOIN 语法(尽管对于像我这样的老家伙来说,它们同样可读)。
    • @Larry Lustig:我同意它们在这种情况下同样具有可读性,但我怀疑有人可能足够老,可以使用与一些 real 混合的数十个隐式连接表来查找查询条件同样可读;-)
    • 我不知道,彼得。我已经很老了。 . .
    • 取决于你的习惯...我看不懂显式连接值得嘘!我想在一个地方查看所有表格。不散。真正的问题是设计问题。他们将 tool_q 与 item_q 分开的事实可能是一个错误。如有必要,它可能应该是一个带有区分列的单个队列表。我敢打赌他们到处都有这样的代码......
    【解决方案3】:

    在你的查询中尽量避免 Subselect 并改用 INNER JOIN

    【讨论】:

    • 除非 INNER JOIN 不起作用。如果您在itemitem_queuetool_queue 之间进行内部连接,您将获得item 的一个子集,您确实不想 想要删除。
    • inner join 不是比 select 语句更贵吗?如果我错了,请纠正我。
    • @Shannon Severance 黄金法则“两次选择一次删除”@sprasad12 让有更多经验的人纠正我,但据我所知,子选择和函数比查询中的加入要慢得多
    • 有了适当的约束,优化器就会为你改变它。没有它们,它可能等同也可能不等同。
    【解决方案4】:

    确保您对引用您的项目表的大表没有约束。在删除的情况下,这可能是一个真正的减速。

    【讨论】:

      【解决方案5】:

      如果不做额外的工作,你真的无法得到一个好的答案。

      在SQL语句本身之后,最重要的是对象(本例中为表和索引)的统计信息具有代表性。

      那么你真的需要看看 oracle 选择的访问路径——有很多方法可以做到这一点。

      试试

      EXPLAIN PLAN SET STATEMENT_IS = 'SQL01' FOR
      DELETE FROM item i 
       WHERE NOT EXISTS (SELECT 1 FROM item_queue q WHERE q.n=i.n) 
       AND NOT EXISTS (SELECT 1 FROM tool_queue t WHERE t.n=i.n);
      

      然后

      SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
      

      也许在这里发布结果。

      当您尝试不同的事情时——例如重写查询、修改索引等,您会注意到访问路径发生了变化。

      这是一个相当复杂的领域 - 您需要学习/练习。

      删除速度慢的原因有很多,但一个重要因素是表上索引的维护。但是,在您的情况下,您说只有 10k 行非常小。 (顺便说一句,你没有在这里给出时间安排。现在需要 1、10 或 100 秒吗?你想达到什么目的?)所以我将专注于通过较大表的访问路径。

      我的第一种方法可能是:

      DELETE FROM item i 
       WHERE NOT EXISTS 
      (SELECT NULL
       FROM item_queue q,
            tool_queue g
        where q.key = g.key      -- if the tables are related
          AND q.n=i.n) ;
      

      但就像我说的,这里有很多因素。

      【讨论】:

        【解决方案6】:

        尝试用SELECT代替delete看看是DELETE操作真正的瓶颈

        【讨论】:

          【解决方案7】:

          虽然它可能不会更快,但如果你这样做会更容易阅读:

          DELETE FROM item i 
          WHERE n NOT IN (SELECT n FROM item_queue)
          AND n NOT IN (SELECT n FROM tool_queue)
          

          【讨论】:

            猜你喜欢
            • 2011-10-15
            • 1970-01-01
            • 2020-11-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多