PostgreSQL中的SQL连接-WHERE子句中的执行计划与ON子句中的不同

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

我们在PostgreSQL 11.9/11.10或12.5中有一个简单的语句,可以使用where子句或ON子句编写连接。含义完全相同,因此返回的行数也相同——但我们收到的解释计划不同。随着表中的数据越来越多,一个执行计划变得非常糟糕,我们想了解为什么PostgreSQL会针对这种情况选择不同的解释计划。有什么想法吗

让我们创建一些示例数据:

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;