2011-03-11 63 views
4

我需要一个ANTI JOIN(不存在从表中选择一些... /左连接表WHERE table.id IS NULL)在同一张表上。 Acutally我有一个索引来服务不存在的问题,但查询计划员选择使用位图堆扫描。Postgres反连接需要表扫描?

表有100万行,所以做了一堆扫描搞砸了......

这将是非常快,如果Postgres的可以比较的indicies。 Postgres是否必须访问这张ANTI-JOIN表格?

我知道表格必须在某个时间点访问以便为MVCC服务,但为什么这么早呢?不能只存在于桌子上,因为它可能会遗漏其他东西?

+1

发布您现在的查询(和计划),以便我们可以分析它并更好地回答问题。可以使用BHS,因为它更高效,但也可能是左连接的结果,可以避免。你可以对查询的不同变体做一个'EXPLAIN ANALYZE',比如'NOT EXISTS','SELECT ... EXCEPT'等等。 – jmz 2011-03-11 18:57:08

+0

这仍然是理论上的。我认为这是不可能的,因为Postgres无法确定索引反连接不会错过某些信息。这就是为什么必须尽早检查表格的原因。 – 2011-03-11 19:03:30

+0

如果是理论上的,为什么你认为PostgreSQL不能做索引扫描? Richard Huxton已经展示了一个使用索引扫描的例子。 – 2011-03-12 09:57:59

回答

7

您需要提供版本详细信息,以及jmz说EXPLAIN ANALYZE输出以获取任何有用的建议。

弗朗兹 - 不要以为是否可能,测试和知道。

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

你看到的将取决于您的版本,并且计划看到统计数据。

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