PostgreSQL支持的子查询优化技术
在第2章,探讨了子查询的优化技术,包括子查询合并、子查询反嵌套、聚集子查询消除三项技术。PostgreSQL只支持子查询反嵌套,其他两种不支持。参看如下示例。
首先我们先做一些前期的准备工作。
创建表结构和索引如下:
CREATE TABLE t1 (a1 INT, b1 INT);
CREATE TABLE t2 (a2 INT, b2 INT);
CREATE TABLE t3 (a3 INT, b3 INT);
CREATE INDEX t_idx_t1 ON t1(a1);
CREATE INDEX t_idx_t2 ON t2(a2);
CREATE INDEX t_idx_t3 ON t3(a3);
插入数据如下:
INSERT INTO t1 values(generate_series(1,10000), generate_series(1,10000));
INSERT INTO t2 values(generate_series(1,10000), generate_series(1,20000,2));
INSERT INTO t3 values(generate_series(1,10000), generate_series(1,30000,3));
示例1 子查询合并技术,PostgreSQL不支持。
两个相似子链接作为条件子句的一部分,查询执行计划如下:
test=# explain SELECT * FROM t1 WHERE a1<10 AND (
EXISTS (SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=1) OR
EXISTS (SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=2) );
QUERY PLAN
----------------------------------------------------------------------------
Result (cost=16.66..25.07 rows=9 width=8)
One-Time Filter: ($0 OR $1)
InitPlan 1 (returns $0)
-> Index Scan using t2_a2_key on t2 (cost=0.00..8.33 rows=1 width=0)
Index Cond: (a2 < 5)
Filter: (b2 = 1) //没有和条件“b2=2”合并在一起判断,而是一个独立结点
InitPlan 2 (returns $1)
-> Index Scan using t2_a2_key on t2 (cost=0.00..8.33 rows=1 width=0)
Index Cond: (a2 < 5)
Filter: (b2 = 2) //没有和条件“b2=1”合并在一起判断,而是一个独立结点
-> Index Scan using t1_a1_key on t1 (cost=0.00..8.41 rows=9 width=8)
Index Cond: (a1 < 10)
(12 行记录)
从查询执行计划看,表t2上执行了两次独立的索引扫描,且过滤条件不同,这表明两条子链接是独立执行的,所以PostgreSQL没有进行子查询的合并。
SQL语句的语义等价于上一条SQL,查询执行计划如下:
test=# explain SELECT * FROM t1 WHERE a1<10 AND EXISTS (SELECT a2 FROM t2 WHERE t2.a2<5 AND (t2.b2=1 OR t2.b2=2));
QUERY PLAN
----------------------------------------------------------------------------
Result (cost=8.34..16.75 rows=9 width=8)
One-Time Filter: $0
InitPlan 1 (returns $0)
-> Index Scan using t2_a2_key on t2 (cost=0.00..8.34 rows=1 width=0)
Index Cond: (a2 < 5)
Filter: ((b2 = 1) OR (b2 = 2))
-> Index Scan using t1_a1_key on t1 (cost=0.00..8.41 rows=9 width=8)
Index Cond: (a1 < 10)
(8 行记录)
查看查询执行计划,在表t2上只执行了一次索引扫描,过滤条件是“(b2 = 1) OR (b2 = 2)”,查询优化器没有把子链接拆分为两个子链接执行而是完全有能力在一个索引扫描中完成子链接的查询工作。对比前一条SQL语句,可知PostgreSQL不支持子查询合并技术。
本文节选自《数据库查询优化器的艺术--原理解析与SQL性能优化》2014年1-2月出版