LIMIT 优化查询

Posted

技术标签:

【中文标题】LIMIT 优化查询【英文标题】:LIMIT optimized queries 【发布时间】:2014-12-27 19:20:45 【问题描述】:

最近我熟悉了 Clojure,我对惰性序列评估的想法感到很有趣,它只在必要时计算值。

我经常使用 PostgreSQL DB,并且在使用 LIMIT 子句时体验到不同的查询性能。例如查询

SELECT * FROM(
    SELECT id FROM foo1
    INTERSECT
    SELECT id FROM foo2) AS subquery
LIMIT 50 

会有相同的执行时间

SELECT id FROM foo1
INTERSECT
SELECT id FROM foo2.

这表明 Postgres 首先评估整个结果,然后只获取前 50 行。这种行为与惰性的想法相反,因为 DB 处理不需要获得最终答案的数据。但另一方面查询

SELECT * FROM foo1 INNER JOIN foo2 ON foo1.id=foo2.id LIMIT 50

性能比

好很多
SELECT * FROM foo1 INNER JOIN foo2 ON foo1.id=foo2.id.

有人知道哪些 Postgres 操作支持这种 LIMIT 惰性吗?

【问题讨论】:

注意:LIMIT 没有 ORDER BY 几乎没有意义。 对于有关查询计划或性能的问题,请总是包括您的 Postgres 版本和您案例的确切表定义。阅读[postgresql-performance] 的标签信息。索引和约束特别重要。 @wildplasser:只要任意选择没问题,LIMIT 可以在没有ORDER BY 的情况下有意义。但是,许多新人并不了解细节,因此我们在 SO 的问题中看到了类似这样的错误查询。 【参考方案1】:

首先,您的查询不等效,除非id 在两个表中都定义为唯一的。 INTERSECT 处理重复项的方式与 INNEROUTER JOIN 不同。缺少的关键字 ALL 使差异更大。 Per documentation:

INTERSECT 的结果不包含任何重复行,除非 指定了ALL 选项。对于ALL,具有m 的行在 左表和n 右表中的重复项将出现min(m,n) 结果集中的次数。

另一方面,连接产生笛卡尔积,即m*n 匹配重复行。所以查询计划不能使用相同的代码路径。

要获得差异较小的结果(但仍然不等效除了唯一的id),请改用:

SELECT id FROM foo1
INTERSECT ALL  -- don't fold dupes
SELECT id FROM foo2
LIMIT 50;

SELECT * FROM foo1 JOIN foo2 USING (id) LIMIT 50; -- return single id column

这里是a fiddle to play with(第 9.3.1 页)。 sqlfiddle.com 上的版本现在已经过时了。而是自己测试最新版本。

很多更多的时间和聪明才智投入到连接的优化中,这些连接更常用几个数量级。我几乎从不使用INTERSECT,因为它经常产生较差的查询计划。在 pg 9.3 的快速测试中,我只能从INTERSECT 中获得顺序扫描,其中连接使用更快的索引扫描。我不知道 pg 9.4 中有任何关于 INTERSECT 的消息。

会有改进的潜力,尤其是在涉及唯一索引的情况下。我想没有人足够关心它,因为INTERSECT 不像其他操作那样受欢迎。

我确实知道 UNION ALLLIMIT 结合使用以从“惰性评估”中受益的非常好的用例:

Way to try multiple SELECTs till a result is available?

【讨论】:

以上是关于LIMIT 优化查询的主要内容,如果未能解决你的问题,请参考以下文章

关于mysql中limit方面的查询优化

如何优化limit

Mysql-Limit 优化

UNION优化limit查询

MySQL 查询优化 - distinct、order by 和 limit

带有 LIMIT 的 SQL UNION 是不是优化了不需要的查询?