2011-03-11 4 views
4

같은 테이블에 ANTI-JOIN이 필요합니다 (테이블에서 SELECT 뭔가를 선택하십시오 .../왼쪽 테이블 WHERE table.id IS NULL). 사실 나는 존재하지 않는 질문을 제공 할 색인이 있지만 쿼리 플래너는 비트 맵 힙 스캔을 사용하기로 선택합니다.Postgres ANTI-JOIN에 테이블 스캔이 필요합니까?

표는 너무 힙 스캔이 포스트 그레스는 모든 인덱스와 비교할 수 있다면

정말 빠른 것 ... 엉망하고, 100 만 개 행이 있습니다. Postgres는이 ANTI-JOIN을 위해 테이블을 방문해야합니까?

MVCC를 제공하기 위해 어느 시점에서 테이블을 방문해야한다는 것을 알고 있습니다. 존재하지 않을 수있는 뭔가가 놓칠 수 있기 때문에 테이블에 의해서만 수정 될 수 있습니까?

+1

우리가 분석하고 더 잘 대답 할 수 있도록 지금 가지고있는 쿼리 (및 계획)를 게시하십시오. BHS가 더 효율적이기 때문에 BHS를 사용할 수도 있지만 왼쪽 결합의 결과 일 수도 있으므로 피할 수 있습니다. 'NOT EXISTS','SELECT ... EXCEPT' 등과 같은 다른 종류의 질의에 대해'EXPLAIN ANALYZE'를 할 수 있습니다. – jmz

+0

여전히 이론적입니다. 포스트 그레스 (Postgres)는 인덱스 안티 조인 (anti-join)이 어떤 정보를 놓치지 않는다는 것을 확신 할 수 없기 때문에 가능하지 않다고 생각한다. 그래서 테이블을 일찍 점검해야합니다. –

+0

이론적인데 왜 PostgreSQL이 인덱스 스캔을 할 수 없다고 생각합니까? Richard Huxton은 이미 색인 스캔을 사용하는 예를 보여주었습니다. –

답변

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 

, 그리고 플래너가 보는 통계.

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? 참조). 그러나 제 경우에는 상황이 악화되었습니다.

set enable_seqscan to off; 

PostgreSQL의 문서는이 ALTER TABLESPACE를 사용하여 seq_page_cost이다 할 수있는 적절한 방법을 말한다

             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) 

내 (해킹) 솔루션은 순차 스캔을 억제하는 것이 었습니다. 인덱스 된 열에서 ORDER BY를 사용하는 경우이 방법을 사용하는 것이 좋지만 잘 모르겠습니다. https://www.postgresql.org/docs/9.1/static/runtime-config-query.html

관련 문제