70.2. 多元统计资料范例

70 .2.1. 功能依赖

可以通过一个非常简单的数据集来证明多变量相关性:一个具有两列的表,这两列都包含相同的值:

CREATE TABLE t (a INT, b INT);
INSERT INTO t SELECT i % 100, i % 100 FROM generate_series(1, 10000) s(i);
ANALYZE t;

Section 14.2中所述,计划者可以使用从pg_class获得的页数和行数来确定t的基数:

SELECT relpages, reltuples FROM pg_class WHERE relname = 't';

 relpages | reltuples
----------+-----------
       45 |     10000

数据分发非常简单;每列中只有 100 个不同的值,并且分布均匀。

以下示例显示了在a列上估算WHERE条件的结果:

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1;
                                 QUERY PLAN                                  
-------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..170.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: (a = 1)
   Rows Removed by Filter: 9900

计划者检查条件并将该子句的选择性确定为 1%。通过比较此估算值和实际的行数,我们可以看到估算值非常准确(实际上是准确的,因为表很小)。将WHERE条件更改为使用b列,将生成相同的计划。但是观察一下,如果我们在两个列上都应用相同的条件,并将它们与AND结合在一起,将会发生什么:

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
                                 QUERY PLAN                                  
-----------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=1 width=8) (actual rows=100 loops=1)
   Filter: ((a = 1) AND (b = 1))
   Rows Removed by Filter: 9900

规划人员分别估算每种条件的选择性,得出与上述相同的 1%估算值。然后假设条件是独立的,因此将其选择性相乘,最终的选择性估计仅为 0.01%。这是一个大大的低估,因为与条件(100)匹配的实际行数高出两个数量级。

可以通过创建一个统计对象来解决此问题,该对象将指示ANALYZE在两列上计算功能相关的多元统计信息:

CREATE STATISTICS stts (dependencies) ON a, b FROM t;
ANALYZE t;
EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
                                  QUERY PLAN                                   
-------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: ((a = 1) AND (b = 1))
   Rows Removed by Filter: 9900

70 .2.2. 多元 N 相异计数

在估计多个列的集合的基数时会发生类似的问题,例如GROUP BY子句将生成的组数。当GROUP BY仅列出一列时,n 差异估计(由 HashAggregate 节点返回的估计行数可见)非常准确:

EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a;
                                       QUERY PLAN                                        
-----------------------------------------------------------------------------------------
 HashAggregate  (cost=195.00..196.00 rows=100 width=12) (actual rows=100 loops=1)
   Group Key: a
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=4) (actual rows=10000 loops=1)

但是,如果没有多变量统计信息,则在GROUP BY中有两列的查询中的组数估计值将降低一个数量级:

EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b;
                                       QUERY PLAN                                        
--------------------------------------------------------------------------------------------
 HashAggregate  (cost=220.00..230.00 rows=1000 width=16) (actual rows=100 loops=1)
   Group Key: a, b
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=8) (actual rows=10000 loops=1)

通过重新定义统计对象以包括两列的 n 个不同的计数,可以大大提高估计值:

DROP STATISTICS stts;
CREATE STATISTICS stts (dependencies, ndistinct) ON a, b FROM t;
ANALYZE t;
EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b;
                                       QUERY PLAN                                        
--------------------------------------------------------------------------------------------
 HashAggregate  (cost=220.00..221.00 rows=100 width=16) (actual rows=100 loops=1)
   Group Key: a, b
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=8) (actual rows=10000 loops=1)