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 不起作用。如果您在item
和item_queue
和tool_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 优化:删除耗时较长的主要内容,如果未能解决你的问题,请参考以下文章