【发布时间】:2020-02-05 09:13:17
【问题描述】:
我正在尝试将一些 ids 传递到排序索引上的子句中,但查询规划器在执行索引搜索后显式地对数据进行排序。以下是我的疑问。
-
生成一个临时表。
SELECT a.n/20 as n, md5(a.n::TEXT) as b INTO temp_table From generate_series(1, 100000) as a(n); -
创建索引
CREATE INDEX idx_temp_table ON temp_table(n ASC, b ASC); -
在下面的查询中,规划器使用索引排序并且没有明确地对数据进行排序。(预期)
EXPLAIN ANALYSE SELECT * from temp_table WHERE n = 10 ORDER BY n, b limit 5;
查询计划
QUERY PLAN Limit (cost=0.42..16.07 rows=5 width=36) (actual time=0.098..0.101 rows=5 loops=1)
-> Index Only Scan using idx_temp_table on temp_table (cost=0.42..1565.17 rows=500 width=36) (actual time=0.095..0.098 rows=5 loops=1)
Index Cond: (n = 10)
Heap Fetches: 5 Planning time: 0.551 ms Execution time: 0.128 ms
-
但是当我使用来自 cte 的一个或多个 id 并将它们传递到子句时,规划器仅使用索引来获取值,但之后显式对它们进行排序(不是预期的)。
EXPLAIN ANALYSE WITH cte(x) AS (VALUES (10)) SELECT * from temp_table WHERE n IN ( SELECT x from cte) ORDER BY n, b limit 5;然后规划器使用下面的查询计划
查询计划
QUERY PLAN
Limit (cost=85.18..85.20 rows=5 width=37) (actual time=0.073..0.075 rows=5 loops=1)
CTE cte
-> Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=4) (actual time=0.001..0.002 rows=2 loops=1)
-> Sort (cost=85.16..85.26 rows=40 width=37) (actual time=0.072..0.073 rows=5 loops=1)
Sort Key: temp_table.n, temp_table.b
Sort Method: top-N heapsort Memory: 25kB
-> Nested Loop (cost=0.47..84.50 rows=40 width=37) (actual time=0.037..0.056 rows=40 loops=1)
-> Unique (cost=0.05..0.06 rows=2 width=4) (actual time=0.009..0.010 rows=2 loops=1)
-> Sort (cost=0.05..0.06 rows=2 width=4) (actual time=0.009..0.010 rows=2 loops=1)
Sort Key: cte.x
Sort Method: quicksort Memory: 25kB
-> CTE Scan on cte (cost=0.00..0.04 rows=2 width=4) (actual time=0.004..0.005 rows=2 loops=1)
-> Index Only Scan using idx_temp_table on temp_table (cost=0.42..42.02 rows=20 width=37) (actual time=0.012..0.018 rows=20 loops=2)
Index Cond: (n = cte.x)
Heap Fetches: 40
Planning time: 0.166 ms
Execution time: 0.101 ms
-
我尝试在 where 子句中传递 id 时进行显式排序,以便保持 id 中的排序顺序,但仍显式排序规划器
EXPLAIN ANALYSE WITH cte(x) AS (VALUES (10)) SELECT * from temp_table WHERE n IN ( SELECT x from cte) ORDER BY n, b limit 5;
查询计划
QUERY PLAN
Limit (cost=42.62..42.63 rows=5 width=37) (actual time=0.042..0.044 rows=5 loops=1)
CTE cte
-> Result (cost=0.00..0.01 rows=1 width=4) (actual time=0.000..0.000 rows=1 loops=1)
-> Sort (cost=42.61..42.66 rows=20 width=37) (actual time=0.042..0.042 rows=5 loops=1)
Sort Key: temp_table.n, temp_table.b
Sort Method: top-N heapsort Memory: 25kB
-> Nested Loop (cost=0.46..42.28 rows=20 width=37) (actual time=0.025..0.033 rows=20 loops=1)
-> HashAggregate (cost=0.05..0.06 rows=1 width=4) (actual time=0.009..0.009 rows=1 loops=1)
Group Key: cte.x
-> Sort (cost=0.03..0.04 rows=1 width=4) (actual time=0.006..0.006 rows=1 loops=1)
Sort Key: cte.x
Sort Method: quicksort Memory: 25kB
-> CTE Scan on cte (cost=0.00..0.02 rows=1 width=4) (actual time=0.003..0.003 rows=1 loops=1)
-> Index Only Scan using idx_temp_table on temp_table (cost=0.42..42.02 rows=20 width=37) (actual time=0.014..0.020 rows=20 loops=1)
Index Cond: (n = cte.x)
Heap Fetches: 20
Planning time: 0.167 ms
Execution time: 0.074 ms
谁能解释为什么规划器对数据使用显式排序?有没有办法绕过这个并使计划者使用索引排序顺序,以便可以保存对记录的额外排序。在生产中,我们有类似的情况,但我们选择的大小太大,但只有少数记录需要分页获取。感谢期待!
【问题讨论】:
-
。 .如果您使用
in子句没有 CTE,查询计划是什么?我怀疑这是问题所在,而不是 CTE。如果是这样,您也许可以使用exists解决问题。 -
@GordonLinoff 查询工作正常,当我在子句中传递值时使用索引顺序,例如。 n IN (10,11) 或 n IN (10)。
-
CTE 在 v12 中已更改。在 PostgreSQL v12 中,您可以根据示例获得所需的计划。 (当然,我不知道它会对您真正感兴趣的查询有什么影响)
标签: postgresql sorting query-optimization common-table-expression postgresql-9.6