SQL 优化:删除耗时较长

Posted

技术标签:

【中文标题】SQL 优化:删除耗时较长【英文标题】:SQL optimization: deletes taking a long time 【发布时间】: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 万行索引

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

【问题讨论】:

请重新格式化您的代码,使其不在滚动窗格中。 【参考方案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)

【讨论】:

以上是关于SQL 优化:删除耗时较长的主要内容,如果未能解决你的问题,请参考以下文章

Hive和Spark SQL优化

oracle如何抓取耗时最多的sql语句或过程

技能提升数据库优化理论实践

MySQL调优笔记——慢SQL优化记录

C++服务编译耗时优化原理及实践

SQL Server 2005:删除优化