PostgreSQL中的SQL连接-WHERE子句中的执行计划与ON子句中的不同
我们在PostgreSQL 11.9/11.10或12.5中有一个简单的语句,可以使用where子句或ON子句编写连接。含义完全相同,因此返回的行数也相同——但我们收到的解释计划不同。随着表中的数据越来越多,一个执行计划变得非常糟糕,我们想了解为什么PostgreSQL会针对这种情况选择不同的解释计划。有什么想法吗 让我们创建一些示例数据:PostgreSQL中的SQL连接-WHERE子句中的执行计划与ON子句中的不同,sql,postgresql,join,where-clause,on-clause,Sql,Postgresql,Join,Where Clause,On Clause,我们在PostgreSQL 11.9/11.10或12.5中有一个简单的语句,可以使用where子句或ON子句编写连接。含义完全相同,因此返回的行数也相同——但我们收到的解释计划不同。随着表中的数据越来越多,一个执行计划变得非常糟糕,我们想了解为什么PostgreSQL会针对这种情况选择不同的解释计划。有什么想法吗 让我们创建一些示例数据: CREATE TABLE t1 ( t1_nr int8 NOT NULL, name varchar(60), CONSTRAIN
CREATE TABLE t1 (
t1_nr int8 NOT NULL,
name varchar(60),
CONSTRAINT t1_pk PRIMARY KEY (t1_nr)
);
INSERT INTO t1 (t1_nr, name) SELECT s, left(md5(random()::text), 10) FROM generate_series(1, 1000000) s; -- 1 million records
CREATE TABLE t2 (
t2_nr int8 NOT NULL,
CONSTRAINT t2_pk PRIMARY KEY (t2_nr)
);
INSERT INTO t2 (t2_nr) SELECT s FROM generate_series(1, 10000000) s; -- 10 million records
CREATE TABLE t3 (
t1_nr int8 NOT NULL,
t2_nr int8 NOT NULL,
CONSTRAINT t3_pk PRIMARY KEY (t2_nr, t1_nr)
);
INSERT INTO t3 (t1_nr, t2_nr) SELECT (s-1)/10+1, s FROM generate_series(1, 10000000) s; -- 10 t2 records per t1 records --> 10 million records
我们的声明和充分分析的统计数据:
EXPLAIN (BUFFERS, ANALYZE)
SELECT t1.*
FROM t1 t1
WHERE EXISTS (
SELECT 1
FROM t3 t3
JOIN t2 t2 ON t2.t2_nr = t3.t2_nr
--AND t3.t1_nr = t1.t1_nr /* GOOD (using ON-CLAUSE) */
WHERE t3.t1_nr = t1.t1_nr /* BAD (using WHERE-CLAUSE) */
)
LIMIT 1000
用“良好”行(ON-子句)解释计划:
用“坏”行(WHERE-子句)解释计划:
谢谢你的想法,如果我们添加一个像
CREATE INDEX t3_t1_nr ON t3(t1_nr);
“坏”的说法会有所改进
但我们的最终解决方案是增加为这些表格收集的统计数据:
ALTER TABLE t1 ALTER COLUMN t1_nr SET STATISTICS 10000;
ALTER TABLE t2 ALTER COLUMN t2_nr SET STATISTICS 10000;
ALTER TABLE t3 ALTER COLUMN t1_nr SET STATISTICS 10000;
ANALYZE t1;
ANALYZE t2;
ANALYZE t3;
更改后,两个选项的执行时间大致相同。
更多信息可在此处找到:能否使用解释(缓冲区、分析)查询的结果?t3上缺少索引。t1\u nr:在t3上创建索引(t1\u nr);这对性能有着巨大的影响。我不知道为什么查询计划(如此)不同,我想在分析之后,这些计划会是相同的。添加外键约束也可能提示计划者。
SET parallel\u tuple\u cost=1代码>将导致相同的计划。在pg-11.3中(默认值为0.1)LIMIT 1000
不带ORDER BY
是不确定的。性能测试可能会产生误导,因为Postgres可以自由返回任意1000行,并且可能会遇到不同的成本。请在外部选择中添加一个订购人
重试。我希望这种差异会消失。
CREATE INDEX t3_t1_nr ON t3(t1_nr);
ALTER TABLE t1 ALTER COLUMN t1_nr SET STATISTICS 10000;
ALTER TABLE t2 ALTER COLUMN t2_nr SET STATISTICS 10000;
ALTER TABLE t3 ALTER COLUMN t1_nr SET STATISTICS 10000;
ANALYZE t1;
ANALYZE t2;
ANALYZE t3;