多元相关性可以通过一个非常简单的数据集来演示 — 一个包含两列的表,两列都含有相同的值:
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 个不同的值,并且均匀分布。
下例展示了对一个WHERE条件进行估计的结果,该条件作用于a列:
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
估计由多个列组成的集合的基数时,也会出现类似问题,例如估计GROUP BY子句产生的分组数。当GROUP BY只列出一个列时,非重复值数量估计(显示为 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)
重新定义统计信息对象,使其包含这两个列的非重复值计数后,估计会有很大改善:
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)