PostgreSQL - максимальна кількість параметрів у пункті "IN"?


147

У Postgres ви можете вказати пункт IN на зразок цього:

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

Хтось знає, яку максимальну кількість параметрів ви можете передати в IN?

Відповіді:


83

Відповідно до вихідного коду, що знаходиться тут, починаючи з рядка 850, PostgreSQL не обмежує явно кількість аргументів.

Далі йде коментар до коду з рядка 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

Це насправді не відповідь на це запитання, однак це може допомогти і іншим.

Принаймні, я можу сказати, що існує технічний ліміт 32767 значень (= Short.MAX_VALUE), який може бути прохідний до сервера PostgreSQL, використовуючи драйвер JDBC Posgresql 9.1.

Це тест "delete from x where id in (... 100k values ​​...)" з драйвером postgresql jdbc:

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
ОП запитав про обмеження двигуна БД, але шукаючи обмеження JDBC, я прийшов сюди, і це те, про що я мав бажання. Отже, обмеження є, проте, досить високим.
9ilsdx 9rvj 0lo

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

ПИТАННЯ ПЛАНУ

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

Але якщо спробувати 2-й запит:

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

ПИТАННЯ ПЛАНУ

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)

Ми можемо бачити, що postgres будує таблицю temp і приєднується до неї


Але те, що я чув, що postgres-9.3 + обидва, здається, є однаковим виконавцем. datadoghq.com/blog/…
PiyusG

18

Не існує обмеження кількості елементів, які ви переходите до пункту IN. Якщо є більше елементів, він вважатиме це масивом, а потім для кожного сканування в базі даних він перевірить, чи міститься він у масиві чи ні. Цей підхід не такий масштабний. Замість використання пункту IN спробуйте використовувати INNER JOIN з таблицею temp. Для отримання додаткової інформації зверніться до http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/ . Використовуючи масштаби INNER JOIN, а також оптимізатор запитів може використовувати хеш-з'єднання та іншу оптимізацію. Тоді як із пунктом IN немає можливості оптимізатору оптимізувати запит. Я помітив прискорення принаймні 2 рази з цією зміною.


2
Посилання, на яке ви посилаєтесь, не говорить про те, про яку СУБД йдеться. Хоча я можу підтвердити, що в Oracle DB використання тимчасових таблиць дає значне підвищення продуктивності над використанням запитів, що поєднуються ORта INпунктують через великі накладні витрати при розборі та плануванні таких запитів, я не зміг підтвердити проблему з Postgres 9.5, дивіться цю відповідь .
блека

17

Як хтось більш досвідчений з Oracle DB, я також був стурбований цим обмеженням. Я провів перевірку ефективності для запиту з параметрами ~ 10'000 в списку IN-10, отримуючи прості числа до 100'000 з таблиці з першими цілими 100000 , фактично перерахувавши всі прості числа як параметри запиту .

Мої результати показують, що вам не потрібно турбуватися про перевантаження оптимізатора плану запитів або отримання планів без використання індексу , оскільки це перетворить запит на використання = ANY({...}::integer[])там, де він може використовувати показники, як очікувалося:

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

Однак ця (досить стара) нитка у списку розсилки pgsql-хакерів вказує на те, що при плануванні таких запитів все ще є незначна вартість, тому прийміть моє слово із зерном солі.


3

Якщо у вас є запит, наприклад:

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

Ви можете збільшити виконання, якщо перезапишете запит, наприклад:

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

10
PostgreSQL, EXPLAINкажуть , що це внутрішньо переписування мої , IN (...)як ANY ('{...}'::integer[]).
Kiran Jonnalagadda

4
У будь-якому випадку, @KiranJonnalagadda, це збільшує продуктивність (мабуть, мізерно), якщо не потрібна внутрішня робота.
Родріго

1

Просто спробував. відповідь -> ціле число поза діапазоном у вигляді 2-байтового значення: 32768


0

Ви можете розглянути можливість рефакторингу цього запиту, а не додавати довільно довгий список ідентифікаторів ... Ви можете використовувати діапазон, якщо ідентифікатори дійсно слідують шаблону у вашому прикладі:

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

Ще один варіант - додати внутрішній вибір:

SELECT * 
FROM user 
WHERE id IN (
    SELECT userId
    FROM ForumThreads ft
    WHERE ft.id = X
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.