2

最近我对Clojure很熟悉,我对懒惰序列评估的想法感到好笑,它只在必要时计算值。LIMIT优化查询

我使用PostgreSQL数据库工作了很多,并且在使用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行。这种行为与懒惰的想法相反,因为数据库过程数据不需要得到最终答案。但在另一方面查询

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的操作支持这样的限制懒惰?

+2

注:'LIMIT'没有'ORDER BY'很少或没有意义。 – wildplasser 2014-12-27 19:23:50

+1

有关查询计划或性能的问题,请总是***包括您的Postgres版本和您的案例的确切表格定义。阅读['[postgresql-performance]'](http://stackoverflow.com/tags/postgresql-performance/info)的标签信息。索引和约束特别相关。 – 2014-12-28 00:13:47

+0

@wildplasser:只要一个*任意*选择是好的,'LIMIT'可以在没有ORDER BY的情况下使用。尽管如此,许多新手并没有意识到细节,所以我们在SO上看到大多数不正确的查询就像这个问题。 – 2014-12-28 01:25:57

回答

1

对于初学者,您的查询是不等于除非id在两个表中都被定义为唯一。 INTERSECT处理与INNEROUTER JOIN不同的重复项。缺失的关键字ALL使差异更大。 Per documentation:

除非指定 ALL选项的INTERSECT结果不包含任何重复的行。用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,因为它通常会产生较差的查询计划。在第9.3页的快速测试中,我只能从INTERSECT中获得连续扫描,其中连接使用更快的索引扫描。我不知道在第9.4节中有关于INTERSECT的任何消息。

有可能改善,尤其是在涉及独特索引的情况下。我想没有人会在意这件事,因为INTERSECT不像其他操作那样受欢迎。

我知道的非常漂亮的用例与LIMIT组合UNION ALL从 “懒惰评估” 中获益,但: