4

我需要在同一张表上的 ANTI-JOIN(不存在 SELECT something from table.../left join table WHERE table.id IS NULL)。Acutally 我有一个索引来服务不存在的问题,但是查询规划器选择使用位图堆扫描。

该表有 1 亿行,因此进行堆扫描会搞砸...

如果 Postgres 可以与这些指标进行比较,那将是非常快的。Postgres 是否必须访问该 ANTI-JOIN 的表?

我知道必须在某个时候访问该表才能为 MVCC 服务,但为什么这么早呢?NOT EXISTS 只能由表来修复,否则它可能会遗漏一些东西?

4

2 回答 2

8

您需要提供版本详细信息,正如 jmz 所说,解释分析输出以获得任何有用的建议。

弗朗茨 - 不要想它是否可能,测试并知道。

这是 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

你看到的将取决于你的版本,以及规划者看到的统计数据。

于 2011-03-11T21:14:35.127 回答
1

我的(简化)查询:

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 会切换到顺序扫描(请参阅为什么 PostgreSQL 对索引列执行顺序扫描?)。然而,就我而言,这让事情变得更糟。

                                                       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)

我的(hack)解决方案是通过以下方式阻止顺序扫描:

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

于 2017-10-09T23:00:16.280 回答