Chỉ mục không được sử dụng với `= any ()` nhưng được sử dụng với` in`


15

Bảng tcó hai chỉ mục:

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)
;

Không có chỉ mục nào được sử dụng với anytoán tử:

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

Nhưng một trong số chúng được sử dụng với intoán tử:

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

Nó sử dụng chỉ mục bản ghi nếu bản ghi được truyền đúng loại:

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

Tại sao trình hoạch định không sử dụng chỉ mục không ghi cho anytoán tử vì nó sử dụng nó cho intoán tử?


Câu hỏi thú vị này xuất hiện từ một cuộc thảo luận liên quan trên SO: stackoverflow.com/a/34601242/939860
Erwin Brandstetter

Câu trả lời:


13

Bên trong, có hai hình thức riêng biệt của IN, cũng như đối với các ANYcấu trúc.

Một trong số đó, lấy một bộ , tương đương với bộ kia và expr IN (<set>)cũng dẫn đến cùng một kế hoạch truy vấn vì expr = ANY(<set>)có thể sử dụng một chỉ mục đơn giản. Chi tiết:

Do đó, hai truy vấn sau là tương đương và cả hai đều có thể sử dụng chỉ mục đơn giản t_a_b_idx(cũng có thể là giải pháp nếu bạn đang cố gắng để truy vấn của mình sử dụng chỉ mục):

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

Hoặc là:

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

Đồng nhất cho cả hai:

                                                        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

Tuy nhiên , điều này không thể dễ dàng được chuyển đến một hàm, vì không có "biến bảng" trong Postgres. Điều này dẫn đến vấn đề bắt đầu chủ đề này:

Có nhiều cách giải quyết khác nhau cho vấn đề đó. Một trong những câu trả lời thay thế tôi đã thêm vào đó. Một số người khác:


Dạng thứ hai của mỗi loại là khác nhau: ANYlấy một mảng thực tế , trong khi INlấy một danh sách các giá trị được phân tách bằng dấu phẩy .

Điều này có hậu quả khác nhau để đầu vào. Như chúng ta có thể thấy trong EXPLAINđầu ra của câu hỏi, mẫu này:

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

được coi là tốc ký cho:

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

Và giá trị ROW thực tế được so sánh. Postgres hiện không đủ thông minh để thấy rằng chỉ số trên loại hỗn hợp t_row_idxcó thể áp dụng được. Nó cũng không nhận ra rằng chỉ số đơn giản t_a_b_idxnên được áp dụng là tốt.

Một diễn viên rõ ràng giúp khắc phục sự thiếu thông minh này:

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

Truyền toán hạng đúng ( ::int_pair[]) là tùy chọn (mặc dù thích hợp hơn cho hiệu suất và để tránh sự mơ hồ). Khi toán hạng bên trái có loại nổi tiếng, toán hạng bên phải được ép từ "bản ghi ẩn danh" sang loại phù hợp. Chỉ sau đó, toán tử được xác định rõ ràng. Và Postgres chọn các chỉ mục áp dụng dựa trên toán tử và toán hạng bên trái . Đối với nhiều toán tử xác định a COMMUTATOR, trình hoạch định truy vấn có thể lật toán hạng để đưa biểu thức được lập chỉ mục sang trái. Nhưng điều đó là không thể với ANYcấu trúc.

Liên quan:

.. các giá trị được lấy dưới dạng các phần tử và Postgres có thể so sánh các giá trị nguyên riêng lẻ như chúng ta có thể thấy trong EXPLAINđầu ra một lần nữa:

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

Do đó Postgres thấy rằng chỉ số đơn giản t_a_b_idxcó thể được sử dụng.


Do đó, sẽ có một giải pháp khác cho trường hợp cụ thể trong ví dụ : vì loại hỗn hợp tùy chỉnh int_pairtrong ví dụ xảy ra tương đương với loại hàng của tchính bảng , chúng ta có thể đơn giản hóa:

CREATE INDEX t_row_idx2 ON t ((t));

Sau đó, truy vấn này sẽ sử dụng chỉ mục mà không cần truyền thêm rõ ràng:

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

Nhưng các trường hợp sử dụng thông thường sẽ không thể sử dụng kiểu bảng hàng hiện có.


1
Một bổ sung nhỏ: trong khi một IN(...)danh sách ngắn có thể được dịch (bởi người lập kế hoạch) thành một ... OR ...biểu thức trong trường hợp trên, nó thường chỉ được dịch sang ANY('{...}'), đó là sử dụng một mảng. Vì vậy, trong hầu hết các trường hợp, INvới một danh sách các giá trị và ANYvới một mảng là điều tương tự.
dezso

1
@dezso: Đối với hầu hết các trường hợp đơn giản, vâng. Câu hỏi cho thấy một trường hợp IN(...) không thể dịch sang = ANY('{...}').
Erwin Brandstetter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.