使用 PostgreSQL 的 EXPLAIN 时,控制节点的出现,对查询复杂度的影响不可忽视。 
Limit PostgreSQL 用户都会使用 LIMIT,因为它非常简单,但让我们来全面描述一下它。LIMIT 操作会执行其下属操作,并仅返回该操作所返回结果中的前 N 行。通常情况下,在返回结果后它会停止下属操作,但在某些情况下(例如在 PL/PgSQL 函数中),当下属操作返回第一行时,该操作可能已经完成了。 
简单示例: 
explain analyze  select   * from  pg_class;                                                QUERY PLAN ---------------------------------------------------------------------------------------------------------  Seq Scan  on  pg_class  (cost = 0.00 . .10 .92 rows = 292  width = 203 ) (actual  time = 0.008 . .0 .047 rows = 295  loops = 1 )  Total runtime:  0.096  ms ( 2 rows ) explain analyze  select * from  pg_class limit  2 ;                                                  QUERY PLAN -------------------------------------------------------------------------------------------------------------  Limit  (cost = 0.00 . .0 .07 rows = 2  width = 203 ) (actual  time = 0.009 . .0 .010 rows = 2  loops = 1 )     - >   Seq Scan  on  pg_class  (cost = 0.00 . .10 .92 rows = 292  width = 203 ) (actual  time = 0.008 . .0 .009 rows = 2  loops = 1 )  Total runtime:  0.045  ms ( 3 rows ) 正如你所看到的,在第二个示例中使用 LIMIT 使得底层的顺序扫描(Seq Scan)在找到两行数据后立即停止了工作。 
Append 该计划只是执行多个下属操作,并将所有返回的行合并为一个结果集返回。 
它用于 UNION/UNION ALL 查询: 
explain  select  oid  from  pg_class union all select  oid  from  pg_proc union all select  oid  from  pg_database;                            QUERY PLAN -----------------------------------------------------------------  Append  (cost = 0.00 . .104 .43 rows = 2943  width = 4 )     - >   Seq Scan  on  pg_class  (cost = 0.00 . .10 .92 rows = 292  width = 4 )     - >   Seq Scan  on  pg_proc  (cost = 0.00 . .92 .49 rows = 2649  width = 4 )     - >   Seq Scan  on  pg_database  (cost = 0.00 . .1 .02 rows = 2  width = 4 ) ( 4 rows ) 在这里你可以看到,Append 操作对三个表执行了三次扫描,并将所有行合并后返回。 
请注意,上面使用的是 UNION ALL。如果我们使用 UNION,结果会是这样: 
explain  select  oid  from  pg_class union select  oid  from  pg_proc union select  oid  from  pg_database;                               QUERY PLAN -----------------------------------------------------------------------  HashAggregate  (cost = 141.22 . .170 .65 rows = 2943  width = 4 )     - >   Append  (cost = 0.00 . .133 .86 rows = 2943  width = 4 )           - >   Seq Scan  on  pg_class  (cost = 0.00 . .10 .92 rows = 292  width = 4 )           - >   Seq Scan  on  pg_proc  (cost = 0.00 . .92 .49 rows = 2649  width = 4 )           - >   Seq Scan  on  pg_database  (cost = 0.00 . .1 .02 rows = 2  width = 4 ) ( 5 rows ) 这是因为 UNION 会去除重复的行:在这种情况下,这是通过哈希聚合(HashAggregate)操作来实现的。 
InitPlan 当查询中的某部分可以(或必须)在其他所有操作之前计算,并且它不依赖于查询其余部分的任何内容时,就会出现这种计划。 
例如,假设你有这样一个查询: 
explain  select   * from  pg_class where  relkind  =     ( select  relkind  from  pg_class  order by  random() limit  1 );                                         QUERY PLAN ------------------------------------------------------------------------------------------  Seq Scan  on  pg_class  (cost = 13.11 . .24 .76 rows = 73  width = 203 )     Filter : (relkind  =  $ 0 )    InitPlan  1  ( returns  $ 0 )       - >   Limit  (cost = 13.11 . .13 .11 rows = 1  width = 1 )             - >   Sort  (cost = 13.11 . .13 .84 rows = 292  width = 1 )                  Sort Key: (random())                   - >   Seq Scan  on  pg_class pg_class_1  (cost = 0.00 . .11 .65 rows = 292  width = 1 ) ( 7 rows ) 在这种情况下,需要先执行 LIMIT / 排序 / 顺序扫描操作,然后再对 pg_class 执行常规的顺序扫描,因为 PostgreSQL 必须将 relkind 值与子查询返回的值进行比较。 
另一方面,如果我这样写: 
explain  select   * , ( select  length( 'redrock' ))  from  pg_class;                          QUERY PLAN -------------------------------------------------------------  Seq Scan  on  pg_class  (cost = 0.01 . .10 .93   rows = 292  width = 203 )    InitPlan  1  ( returns  $ 0 )       - >    Result   (cost = 0.00 . .0 .01   rows = 1  width = 0 ) ( 3   rows ) PostgreSQL 会正确地发现子查询的列不依赖于 pg_class 表中的任何数据,因此它只需运行一次,不必为每一行重新计算长度。 
当然,你可以有多个初始计划,如下所示: 
explain  select   * , ( select  length( 'redrock' ))  from  pg_class where  relkind  =     ( select  relkind  from  pg_class  order by  random() limit  1 );                                         QUERY PLAN ------------------------------------------------------------------------------------------  Seq Scan  on  pg_class  (cost = 13.12 . .24 .77 rows = 73  width = 203 )     Filter : (relkind  =  $ 1 )    InitPlan  1  ( returns  $ 0 )       - > Result   (cost = 0.00 . .0 .01 rows = 1  width = 0 )    InitPlan  2  ( returns  $ 1 )       - >   Limit  (cost = 13.11 . .13 .11 rows = 1  width = 1 )             - >   Sort  (cost = 13.11 . .13 .84 rows = 292  width = 1 )                  Sort Key: (random())                   - >   Seq Scan  on  pg_class pg_class_1  (cost = 0.00 . .11 .65 rows = 292  width = 1 ) ( 9 rows ) 不过,有一个重要的点是:单个查询中初始计划的编号是 “全局的”,而不是 “按操作的”。 
SubPlan 子计划(SubPlan)与嵌套循环(NestedLoop)有点相似,因为它们都可能被多次调用。 
调用子计划是为了计算来自子查询的数据,而该子查询实际上依赖于当前行。 
例如: 
explain analyze  select  c.relname, c.relkind,   ( select count ( * )  from  pg_class x  where  c.relkind  =  x.relkind) from  pg_class c;                                                       QUERY PLAN -----------------------------------------------------------------------------------------------------------------------  Seq Scan  on  pg_class c  (cost = 0.00 . .3468 .93 rows = 292  width = 65 ) (actual  time = 0.135 . .26 .717 rows = 295  loops = 1 )    SubPlan  1       - >   Aggregate  (cost = 11.83 . .11 .84 rows = 1  width = 0 ) (actual  time = 0.090 . .0 .090 rows = 1  loops = 295 )             - >   Seq Scan  on  pg_class x  (cost = 0.00 . .11 .65 rows = 73  width = 0 ) (actual  time = 0.010 . .0 .081 rows = 93  loops = 295 )                   Filter : (c.relkind  =  relkind)                   Rows  Removed  by Filter :  202  Total runtime:  26.783  ms ( 7 rows ) 对于 “pg_class as c” 的扫描所返回的每一行,PostgreSQL 都必须运行子计划,该子计划会检查 pg_class 中有多少行在 relkind 列上具有与当前处理行相同的值。 
请注意,“Seq Scan on pg_class x” 行中的 “loops=295”,与前面 “Seq Scan on pg_class c” 节点中的 “rows=295” 是相匹配的。 
转载