PostgreSQL - số lượng tham số tối đa trong mệnh đề IN IN?


147

Trong Postgres, bạn có thể chỉ định mệnh đề IN, như thế này:

SELECT * FROM user WHERE id IN (1000, 1001, 1002)

Có ai biết số lượng tham số tối đa bạn có thể truyền vào IN là bao nhiêu không?

Câu trả lời:


83

Theo mã nguồn được đặt ở đây, bắt đầu từ dòng 850, PostgreSQL không giới hạn rõ ràng số lượng đối số.

Sau đây là nhận xét mã từ dòng 870:

/*
 * We try to generate a ScalarArrayOpExpr from IN/NOT IN, but this is only
 * possible if the inputs are all scalars (no RowExprs) and there is a
 * suitable array type available.  If not, we fall back to a boolean
 * condition tree with multiple copies of the lefthand expression.
 * Also, any IN-list items that contain Vars are handled as separate
 * boolean conditions, because that gives the planner more scope for
 * optimization on such clauses.
 *
 * First step: transform all the inputs, and detect whether any are
 * RowExprs or contain Vars.
 */

56

Đây không thực sự là một câu trả lời cho câu hỏi hiện tại, tuy nhiên nó cũng có thể giúp những người khác.

Ít nhất tôi có thể biết có giới hạn kỹ thuật là 32767 giá trị (= Short.MAX_VALUE) có thể chuyển sang phần phụ trợ PostgreQuery, sử dụng trình điều khiển JDBC của Posgresql 9.1.

Đây là một thử nghiệm "xóa khỏi x trong đó id trong (... 100k giá trị ...)" với trình điều khiển jdbc postgresql:

Caused by: java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: 100000
    at org.postgresql.core.PGStream.SendInteger2(PGStream.java:201)

6
OP đã hỏi về giới hạn động cơ DB, nhưng tìm kiếm giới hạn JDBC tôi đã đến đây và đó là những gì tôi đang lờ mờ. Vì vậy, có một hạn chế, tuy nhiên, khá cao.
9ilsdx 9rvj 0lo

36
explain select * from test where id in (values (1), (2));

KẾ HOẠCH

 Seq Scan on test  (cost=0.00..1.38 rows=2 width=208)
   Filter: (id = ANY ('{1,2}'::bigint[]))

Nhưng nếu thử truy vấn thứ 2:

explain select * from test where id = any (values (1), (2));

KẾ HOẠCH

Hash Semi Join  (cost=0.05..1.45 rows=2 width=208)
       Hash Cond: (test.id = "*VALUES*".column1)
       ->  Seq Scan on test  (cost=0.00..1.30 rows=30 width=208)
       ->  Hash  (cost=0.03..0.03 rows=2 width=4)
             ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=4)

Chúng ta có thể thấy rằng postgres xây dựng bảng tạm thời và tham gia với nó


Nhưng những gì tôi nghe nói rằng postgres-9.3 + cả hai dường như là cùng một biểu diễn. datadoghq.com/blog/ từ
PiyusG

18

Không có giới hạn về số lượng phần tử mà bạn đang chuyển đến mệnh đề IN. Nếu có nhiều phần tử hơn, nó sẽ coi nó là mảng và sau đó cho mỗi lần quét trong cơ sở dữ liệu, nó sẽ kiểm tra xem nó có được chứa trong mảng hay không. Cách tiếp cận này không phải là có thể mở rộng. Thay vì sử dụng mệnh đề IN, hãy thử sử dụng INNER THAM GIA với bảng tạm thời. Tham khảo http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-probolsatic/ để biết thêm. Sử dụng thang đo INNER THAM GIA cũng như trình tối ưu hóa truy vấn có thể sử dụng phép nối băm và tối ưu hóa khác. Trong khi đó với mệnh đề IN thì không có cách nào để trình tối ưu hóa tối ưu hóa truy vấn. Tôi đã nhận thấy tốc độ tăng ít nhất gấp 2 lần với thay đổi này.


2
Liên kết mà bạn đang đề cập không nói DBMS đang nói về cái gì. Mặc dù tôi có thể xác nhận rằng trên Oracle DB, việc sử dụng các bảng tạm thời giúp tăng hiệu suất lớn hơn so với sử dụng các truy vấn kết hợp ORINmệnh đề do chi phí lớn trong việc phân tích cú pháp và lập kế hoạch cho các truy vấn đó, tôi không thể xác nhận vấn đề với Postgres 9.5, hãy xem câu trả lời này .
blubb

17

Là một người có nhiều kinh nghiệm hơn với Oracle DB, tôi cũng lo ngại về giới hạn này. Tôi đã thực hiện kiểm tra hiệu năng cho một truy vấn có ~ 10'000 tham số trong một danh sách IN, tìm nạp các số nguyên tố lên tới 100'000 từ một bảng có số nguyên 100'000 đầu tiên bằng cách liệt kê thực sự tất cả các số nguyên tố làm tham số truy vấn .

Kết quả của tôi cho thấy rằng bạn không cần lo lắng về việc quá tải trình tối ưu hóa kế hoạch truy vấn hoặc nhận các kế hoạch mà không sử dụng chỉ mục , vì nó sẽ chuyển đổi truy vấn để sử dụng = ANY({...}::integer[])nơi có thể tận dụng các chỉ số như mong đợi:

-- prepare statement, runs instantaneous:
PREPARE hugeplan (integer, integer, integer, ...) AS
SELECT *
FROM primes
WHERE n IN ($1, $2, $3, ..., $9592);

-- fetch the prime numbers:
EXECUTE hugeplan(2, 3, 5, ..., 99991);

-- EXPLAIN ANALYZE output for the EXECUTE:
"Index Scan using n_idx on primes  (cost=0.42..9750.77 rows=9592 width=5) (actual time=0.024..15.268 rows=9592 loops=1)"
"  Index Cond: (n = ANY ('{2,3,5,7, (...)"
"Execution time: 16.063 ms"

-- setup, should you care:
CREATE TABLE public.primes
(
  n integer NOT NULL,
  prime boolean,
  CONSTRAINT n_idx PRIMARY KEY (n)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE public.primes
  OWNER TO postgres;

INSERT INTO public.primes
SELECT generate_series(1,100000);

Tuy nhiên, chủ đề (khá cũ) này trong danh sách gửi thư của tin tặc pssql chỉ ra rằng vẫn còn một chi phí không đáng kể trong việc lập kế hoạch cho các truy vấn như vậy, vì vậy hãy hiểu ý tôi bằng một hạt muối.


3

Nếu bạn có truy vấn như:

SELECT * FROM user WHERE id IN (1, 2, 3, 4 -- and thousands of another keys)

bạn có thể tăng hiệu suất nếu viết lại truy vấn của bạn như:

SELECT * FROM user WHERE id = ANY(VALUES (1), (2), (3), (4) -- and thousands of another keys)

10
PostgreQuery EXPLAINnói rằng nó viết lại nội bộ của tôi IN (...)như là ANY ('{...}'::integer[]).
Kiran Jonnalagadda

4
Dù sao, @KiranJonnalagadda, nó tăng hiệu suất (không đáng kể, có lẽ) nếu không có công việc nội bộ là cần thiết.
Rodrigo

1

Chỉ cần thử nó. câu trả lời là -> số nguyên nằm ngoài phạm vi dưới dạng giá trị 2 byte: 32768


0

Bạn có thể muốn xem xét tái cấu trúc truy vấn đó thay vì thêm một danh sách id dài tùy ý ... Bạn có thể sử dụng một phạm vi nếu các id thực sự tuân theo mẫu trong ví dụ của bạn:

SELECT * FROM user WHERE id >= minValue AND id <= maxValue;

Một tùy chọn khác là thêm một lựa chọn bên trong:

SELECT * 
FROM user 
WHERE id IN (
    SELECT userId
    FROM ForumThreads ft
    WHERE ft.id = X
);
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.