=> EXPLAIN (costs off) SELECT *
FROM tickets t
JOIN ticket_flights tf ON tf.ticket_no = t.ticket_no
ORDER BY t.ticket_no;
QUERY PLAN
----------------------------------------------------------------
Merge Join
Merge Cond: (t.ticket_no = tf.ticket_no)
-> Index Scan using tickets_pkey on tickets t
-> Index Scan using ticket_flights_pkey on ticket_flights tf
(4 rows)
优化器选择此连接方式,因为它恰好返回了 ORDER BY 子句中指定的顺序。在处理计划时,优化器会考虑数据集的排序顺序,如果不需要则不会执行显式排序。特别是,通过合并连接获得的行集可以用于下一个合并连接,只要其排序顺序合适:
=> EXPLAIN (costs off) SELECT *
FROM tickets t
JOIN ticket_flights tf ON t.ticket_no = tf.ticket_no
JOIN boarding_passes bp ON bp.ticket_no = tf.ticket_no
AND bp.flight_id = tf.flight_id
ORDER BY t.ticket_no;
QUERY PLAN
---------------------------------------------------------------------
Merge Join
Merge Cond: ((t.ticket_no = tf.ticket_no) AND (bp.flight_id = ...
-> Merge Join
Merge Cond: (bp.ticket_no = t.ticket_no)
-> Index Scan using boarding_passes_pkey on boarding_passe...
-> Index Scan using tickets_pkey on tickets t
-> Index Scan using ticket_flights_pkey on ticket_flights tf
(7 rows)
=> EXPLAIN SELECT *
FROM tickets t
JOIN ticket_flights tf ON tf.ticket_no = t.ticket_no
ORDER BY t.ticket_no;
QUERY PLAN
----------------------------------------------------------------
Merge Join (cost=0.99..822507.74 rows=8391852 width=136)
Merge Cond: (t.ticket_no = tf.ticket_no)
-> Index Scan using tickets_pkey on tickets t
(cost=0.43..139136.22 rows=2949857 width=104)
-> Index Scan using ticket_flights_pkey on ticket_flights tf
(cost=0.56..571098.73 rows=8391852 width=32)
(6 rows)
=> EXPLAIN (costs off) SELECT *
FROM tickets t
FULL JOIN ticket_flights tf ON tf.ticket_no = t.ticket_no
ORDER BY t.ticket_no;
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: t.ticket_no
-> Merge Full Join
Merge Cond: (t.ticket_no = tf.ticket_no)
-> Index Scan using tickets_pkey on tickets t
-> Index Scan using ticket_flights_pkey on ticket_flights tf
(6 rows)
=> EXPLAIN (costs off)
SELECT *
FROM flights f
JOIN airports_data dep ON f.departure_airport = dep.airport_code
ORDER BY dep.airport_code;
QUERY PLAN
--------------------------------------------------------
Merge Join
Merge Cond: (f.departure_airport = dep.airport_code)
-> Sort
Sort Key: f.departure_airport
-> Seq Scan on flights f
-> Sort
Sort Key: dep.airport_code
-> Seq Scan on airports_data dep
(8 rows)
这种排序也可在连接上下文之外使用,例如在 ORDER BY 子句中,无论是单独使用还是作为窗口函数的一部分:
=> EXPLAIN (costs off)
SELECT flight_id,
row_number() OVER (PARTITION BY flight_no ORDER BY flight_id)
FROM flights f;
QUERY PLAN
---------------------------------------------------------------------
WindowAgg
Window: w1 AS (PARTITION BY flight_no ORDER BY flight_id ROWS ...
-> Sort
Sort Key: flight_no, flight_id
-> Seq Scan on flights f
(5 rows)
=> EXPLAIN (analyze, costs off, timing off, summary off, buffers off)
SELECT *
FROM bookings
ORDER BY total_amount, book_date;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort (actual rows=2111110.00 loops=1)
Sort Key: total_amount, book_date
Presorted Key: total_amount
Full-sort Groups: 2823 Sort Method: quicksort Average
Memory: 27kB Peak Memory: 27kB
Pre-sorted Groups: 2624 Sort Method: quicksort Average
Memory: 1953kB Peak Memory: 2014kB
-> Index Scan using bookings_total_amount_idx on bookings (ac...
Index Searches: 1
(9 rows)
从计划中可以看出,行集已按 total_amount 排序,因为它是通过按该列构建的索引扫描得到的(Presorted Key)。EXPLAIN ANALYZE 命令还显示了执行时间统计信息。Full-sort Groups 行对应于被合并并完全排序的小组,Presorted Groups 行对应于较大的组,这些组在 book_date 列上进行了二次排序。两种情况下均使用了内存中的快速排序。不同大小的组的存在是由于预订成本分布不均所致。
增量排序也可用于窗口函数的计算:(v. 14)
=> EXPLAIN (costs off)
SELECT row_number() OVER (ORDER BY total_amount, book_date)
FROM bookings;
QUERY PLAN
---------------------------------------------------------------------
WindowAgg
Window: w1 AS (ORDER BY total_amount, book_date ROWS UNBOUNDED...
-> Incremental Sort
Sort Key: total_amount, book_date
Presorted Key: total_amount
-> Index Scan using bookings_total_amount_idx on bookings
(6 rows)
=> EXPLAIN (costs off) SELECT DISTINCT book_ref
FROM bookings
ORDER BY book_ref;
QUERY PLAN
------------------------------------------------------
Unique
-> Index Only Scan using bookings_pkey on bookings
(2 rows)
Unique 节点可以参与并行计划,用于消除工作进程返回列表中的重复项:(v. 15)
=> EXPLAIN (costs off) SELECT DISTINCT fare_conditions
FROM ticket_flights;
QUERY PLAN
------------------------------------------------------
Unique
-> Gather Merge
Workers Planned: 2
-> Sort
Sort Key: fare_conditions
-> HashAggregate
Group Key: fare_conditions
-> Parallel Seq Scan on ticket_flights
(8 rows)
=> EXPLAIN (costs off) SELECT book_ref, count(*)
FROM bookings
GROUP BY book_ref
ORDER BY book_ref;
QUERY PLAN
------------------------------------------------------
GroupAggregate
Group Key: book_ref
-> Index Only Scan using bookings_pkey on bookings
(3 rows)