`= any ()`와 함께 사용되지 않지만`in`과 함께 사용되는 색인


15

테이블 t에는 두 개의 인덱스가 있습니다.

create table t (a int, b int);
create type int_pair as (a int, b int);
create index t_row_idx on t (((a,b)::int_pair));
create index t_a_b_idx on t (a,b);

insert into t (a,b)
select i, i
from generate_series(1, 100000) g(i)
;

any연산자 와 함께 색인이 사용되지 않습니다 .

explain analyze
select *
from t
where (a,b) = any(array[(1,1),(1,2)])
;
                                            QUERY PLAN                                             
---------------------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..1693.00 rows=1000 width=8) (actual time=0.042..126.789 rows=1 loops=1)
   Filter: (ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
   Rows Removed by Filter: 99999
 Planning time: 0.122 ms
 Execution time: 126.836 ms

그러나 그중 하나가 in연산자 와 함께 사용됩니다 .

explain analyze
select *
from t
where (a,b) in ((1,1),(1,2))
;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using t_a_b_idx on t  (cost=0.29..8.32 rows=1 width=8) (actual time=0.028..0.029 rows=1 loops=1)
   Index Cond: (a = 1)
   Filter: ((b = 1) OR (b = 2))
   Heap Fetches: 1
 Planning time: 0.161 ms
 Execution time: 0.066 ms

레코드가 올바른 유형으로 캐스트 된 경우 레코드 색인을 사용합니다.

explain analyze
select *
from t
where (a,b)::int_pair = any(array[row(1,1),row(1,2)])
;
                                                  QUERY PLAN                                                  
--------------------------------------------------------------------------------------------------------------
 Index Scan using t_row_idx on t  (cost=0.42..12.87 rows=2 width=8) (actual time=0.106..0.126 rows=1 loops=1)
   Index Cond: (ROW(a, b)::int_pair = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
 Planning time: 0.208 ms
 Execution time: 0.203 ms

플래너가 any연산자에 사용할 때 레코드가 아닌 인덱스를 연산자에 사용하지 않는 이유는 무엇 in입니까?


이 흥미로운 질문은 SO에 대한 관련 논의에서 등장 stackoverflow.com/a/34601242/939860
어윈 Brandstetter

답변:


13

내부적으로, 거기에 두 개의 분리 된 형태IN뿐만 아니라에 대한 ANY구조는.

각각의 하나는 set을 취하고 다른 하나와 expr IN (<set>)동일 expr = ANY(<set>)하며 일반 인덱스를 사용할 수 있는 것과 동일한 쿼리 계획으로 이어집니다 . 세부:

결과적으로 다음 두 쿼리는 동일하며 둘 다 일반 인덱스 를 사용할 수 있습니다 (쿼리를 사용하여 쿼리를 가져 오려는 경우 솔루션t_a_b_idx 이 될 수도 있음 ).

EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));

또는:

...
WHERE (a,b) IN (VALUES (1,1),(1,2));

둘 다 동일합니다.

                                                        QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.33..16.71 rows=1 width=8) (actual time=0.101..0.101 rows=0 loops=1)
   ->  Unique  (cost=0.04..0.05 rows=2 width=8) (actual time=0.068..0.070 rows=2 loops=1)
         ->  Sort  (cost=0.04..0.04 rows=2 width=8) (actual time=0.067..0.068 rows=2 loops=1)
               Sort Key: "*VALUES*".column1, "*VALUES*".column2
               Sort Method: quicksort  Memory: 25kB
               ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=8) (actual time=0.005..0.005 rows=2 loops=1)
   ->  Index Only Scan using t_plain_idx on t  (cost=0.29..8.32 rows=1 width=8) (actual time=0.009..0.009 rows=0 loops=2)
         Index Cond: ((a = "*VALUES*".column1) AND (b = "*VALUES*".column2))
         Heap Fetches: 0
 Planning time: 4.080 ms
 Execution time: 0.202 ms

그러나 Postgres에는 "테이블 변수"가 없으므로 함수에 쉽게 전달할 수 없습니다. 이 주제를 시작한 문제로 이어집니다.

해당 문제에 대한 다양한 해결 방법이 있습니다. 하나는 내가 거기에 추가 한 대안 답변입니다. 다른 사람들 :


각각의 두 번째 형식은 다릅니다 : ANY실제 배열 을 사용 IN하고 쉼표로 구분 된 값 목록을 사용합니다 .

입력입력 할 때 다른 결과가 나타납니다 . EXPLAIN질문 의 결과 에서 볼 수 있듯이이 양식은 다음과 같습니다.

WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);

다음과 같은 속기입니다.

ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])

그리고 실제 ROW 값이 비교됩니다. Postgres는 현재 복합 유형의 색인을 볼만큼 똑똑하지 않습니다.t_row_idx 이 적용 가능한지 . 단순한 인덱스 t_a_b_idx도 적용 할 수 있어야 한다는 것도 알지 못합니다 .

명시 적 캐스트는 이러한 스마트 부족을 극복하는 데 도움이됩니다.

WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);

올바른 피연산자 ( ::int_pair[])를 캐스팅하는 것은 선택 사항입니다 (성능과 모호성을 피하기 위해 선호 됨). 왼쪽 피연산자가 잘 알려진 유형을 가지면 오른쪽 피연산자가 "익명 레코드"에서 일치하는 유형으로 강제됩니다. 그래야만 연산자가 명확하게 정의됩니다. 그리고 Postgres는 연산자왼쪽 피연산자를 기준으로 해당 인덱스를 선택 합니다. 을 정의하는 많은 연산자의 COMMUTATOR경우 쿼리 플래너는 피연산자를 뒤집어 인덱스 식을 왼쪽으로 가져올 수 있습니다. 그러나 ANY구문 으로는 불가능합니다 .

관련 :

.. 값은 요소 로 간주 되며 Postgres는 EXPLAIN출력에서 한 번 더 볼 수 있듯이 개별 정수 값을 비교할 수 있습니다 .

Filter: ((b = 1) OR (b = 2))

따라서 Postgres는 간단한 색인을 t_a_b_idx사용할 수 있음을 발견했습니다 .


결과적으로,이 예에서 특별한 경우에 대한 또 다른 해결책 이있을 것 입니다. 예 에서 사용자 정의 복합 유형 int_pair은 테이블 t자체 의 행 유형과 동일하므로 다음과 같이 단순화 할 수 있습니다.

CREATE INDEX t_row_idx2 ON t ((t));

그런 다음이 쿼리는 더 명시적인 캐스팅없이 인덱스를 사용합니다.

EXPLAIN ANALYZE
SELECT *
FROM   t
WHERE  t = ANY(ARRAY[(1,1),(1,2)]);
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=40.59..496.08 rows=1000 width=8) (actual time=0.19
1..0.191 rows=0 loops=1)
   Recheck Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
   ->  Bitmap Index Scan on t_row_idx2  (cost=0.00..40.34 rows=1000 width=0) (actual time=0.188..0.188 rows=0 loops=1)
         Index Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
 Planning time: 2.575 ms
 Execution time: 0.267 ms

그러나 일반적인 유스 케이스는 내재적으로 존재하는 테이블 행 유형을 활용할 수 없습니다.


1
작은 추가 사항 : 짧은 IN(...)목록은 (계획자에 의해) ... OR ...위의 경우 표현식 으로 변환 될 수 있지만 일반적으로 ANY('{...}')배열을 사용하여 로 변환됩니다 . 따라서 대부분의 경우 IN값 목록과 ANY배열이 동일합니다.
dezso

1
@dezso : 가장 간단한 경우입니다. 이 질문은 로 번역 IN(...) 할 수없는 경우를 보여줍니다 = ANY('{...}').
Erwin Brandstetter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.