我在同一个表上需要一个ANTI-JOIN(不存在来自表的SELECT东西...... /左连接表WHERE table.id IS NULL)。实际上我有一个索引来提供不存在的问题,但查询规划器选择使用位图堆扫描。
该表有1亿行,因此堆栈扫描搞砸了......
如果Postgres可以与指标进行比较,那将会非常快。 Postgres是否必须访问此ANTI-JOIN的表格?
我知道必须在某个时候访问该表以服务MVCC,但为什么这么早呢?不能只用桌子来固定,因为它可能会遗漏一些东西吗?
答案 0 :(得分:7)
您需要提供版本详细信息,并且jmz说EXPLAIN ANALYZE输出以获取任何有用的建议。
弗兰兹 - 不要以为是否可能,测试和了解。
这是v9.0:
CREATE TABLE tl (i int, t text);
CREATE TABLE tr (i int, t text);
INSERT INTO tl SELECT s, 'text ' || s FROM generate_series(1,999999) s;
INSERT INTO tr SELECT s, 'text ' || s FROM generate_series(1,999999) s WHERE s % 3 = 0;
ALTER TABLE tl add primary key (i);
CREATE INDEX tr_i_idx ON tr (i);
ANALYSE;
EXPLAIN ANALYSE SELECT i,t FROM tl LEFT JOIN tr USING (i) WHERE tr.i IS NULL;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Merge Anti Join (cost=0.95..45611.86 rows=666666 width=15) (actual time=0.040..4011.970 rows=666666 loops=1)
Merge Cond: (tl.i = tr.i)
-> Index Scan using tl_pkey on tl (cost=0.00..29201.32 rows=999999 width=15) (actual time=0.017..1356.996 rows=999999 lo
-> Index Scan using tr_i_idx on tr (cost=0.00..9745.27 rows=333333 width=4) (actual time=0.015..439.087 rows=333333 loop
Total runtime: 4602.224 ms
您看到的内容取决于您的版本以及规划人员看到的统计数据。
答案 1 :(得分:0)
我的(简化)查询:
SELECT a.id FROM a LEFT JOIN b ON b.id = a.id WHERE b.id IS NULL ORDER BY id;
像这样的查询计划有效:
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Merge Anti Join (cost=0.57..3831.88 rows=128092 width=8)
Merge Cond: (a.id = b.id)
-> Index Only Scan using a_pkey on a (cost=0.42..3399.70 rows=130352 width=8)
-> Index Only Scan using b_pkey on b (cost=0.15..78.06 rows=2260 width=8)
(4 rows)
但是,如果规划人员认为它可能更好,有时postgresql 9.5.9会切换到顺序扫描(参见Why does PostgreSQL perform sequential scan on indexed column?)。但是,在我的情况下,它使事情变得更糟。
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Merge Anti Join (cost=405448.22..39405858.08 rows=1365191502 width=8)
Merge Cond: (a.id = b.id)
-> Index Only Scan using a_pkey on a (cost=0.58..35528317.86 rows=1368180352 width=8)
-> Materialize (cost=405447.64..420391.89 rows=2988850 width=8)
-> Sort (cost=405447.64..412919.76 rows=2988850 width=8)
Sort Key: b.id
-> Seq Scan on b (cost=0.00..43113.50 rows=2988850 width=8)
(7 rows)
我的(黑客)解决方案是阻止顺序扫描:
set enable_seqscan to off;
postgresql文档说正确的方法是使用ALTER TABLESPACE对seq_page_cost执行此操作。在索引列上使用ORDER BY时,这可能是明智之举,但我不确定。 https://www.postgresql.org/docs/9.1/static/runtime-config-query.html