我在 postgresql 9.2 系统上有一个查询,它的正常形式大约需要 20 秒,但使用 CTE 时只需要大约 120 毫秒。
为简洁起见,我简化了这两个查询。
这是正常形式(大约需要 20 秒):
SELECT *
FROM tableA
WHERE (columna = 1 OR columnb = 2) AND
atype = 35 AND
aid IN (1, 2, 3)
ORDER BY modified_at DESC
LIMIT 25;
这是此查询的解释: http: //explain.depesz.com/s/2v8
CTE表格(约120ms):
WITH raw AS (
SELECT *
FROM tableA
WHERE (columna = 1 OR columnb = 2) AND
atype = 35 AND
aid IN (1, 2, 3)
)
SELECT *
FROM raw
ORDER BY modified_at DESC
LIMIT 25;
这是 CTE 的解释: http: //explain.depesz.com/s/uxy
只需将 移动ORDER BY
到查询的外部即可将成本降低 99%。
我有两个问数据?
关于上述问题,是否有其他统计信息或其他规划器提示有助于提高第一个查询的性能?
编辑:取消限制也会导致查询使用堆扫描,而不是向后的索引扫描。没有LIMIT
查询在 40 毫秒内完成。
在看到LIMIT
我尝试使用LIMIT 1
,LIMIT 2
等的效果后。使用时查询在 100ms 内执行,LIMIT 1
并且 10s+ LIMIT
> 1。
在考虑了更多之后,问题 2 归结为为什么规划器在一种情况下使用向后索引扫描,而在另一种逻辑等效情况下使用位图堆扫描 + 排序?在这两种情况下,我如何“帮助”计划者使用有效的计划?
更新:我接受了克雷格的回答,因为它是最全面和最有帮助的。我最终解决问题的方法是使用实际上等效但在逻辑上不等效的查询。问题的根源在于对 modified_at 上的索引进行了索引扫描。为了通知计划者这不是一个好主意,我添加了一个谓词形式WHERE modified_at >= NOW() - INTERVAL '1 year'
。这为应用程序包含了足够的数据,但阻止了规划器沿着向后索引扫描路径前进。
这是一个影响小得多的解决方案,它避免了使用子查询或 CTE 重写查询的需要。YMMV。