In Postgres, puoi specificare una clausola IN, come questa:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
Qualcuno sa qual è il numero massimo di parametri che è possibile passare in IN?
In Postgres, puoi specificare una clausola IN, come questa:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
Qualcuno sa qual è il numero massimo di parametri che è possibile passare in IN?
Risposte:
Secondo il codice sorgente che si trova qui, a partire dalla riga 850, PostgreSQL non limita esplicitamente il numero di argomenti.
Di seguito è riportato un commento in codice dalla riga 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.
*/
Questa non è davvero una risposta alla domanda attuale, tuttavia potrebbe aiutare anche gli altri.
Almeno posso dire che esiste un limite tecnico di 32767 valori (= Short.MAX_VALUE) passabili al backend PostgreSQL, usando il driver JDBC di Posgresql 9.1.
Questo è un test di "elimina da x dove id in (... 100k valori ...)" con il driver 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)
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[]))
Ma se provi la seconda query:
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)
Possiamo vedere che Postgres costruisce una tabella temporanea e ci unisce
Non vi è alcun limite al numero di elementi che si sta passando alla clausola IN. Se ci sono più elementi lo considererà come array e quindi per ogni scansione nel database controllerà se è contenuto nell'array o meno. Questo approccio non è così scalabile. Invece di utilizzare la clausola IN, provare a utilizzare INNER JOIN con la tabella temporanea. Fare riferimento a http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/ per ulteriori informazioni. L'uso delle scale INNER JOIN e di Query Optimizer possono utilizzare hash join e altre ottimizzazioni. Considerando che con la clausola IN non c'è modo per l'ottimizzatore di ottimizzare la query. Ho notato uno speedup di almeno 2 volte con questa modifica.
OR
e IN
clausole a causa dell'elevato sovraccarico nell'analisi e nella pianificazione di tali query, non ho potuto confermare il problema con Postgres 9.5, vedere questa risposta .
Come qualcuno più esperto con Oracle DB, ero preoccupato anche per questo limite. Ho eseguito un test delle prestazioni per una query con ~ 10'000 parametri in un IN
elenco, recuperando i numeri primi fino a 100.000 da una tabella con i primi 100.000 numeri interi elencando effettivamente tutti i numeri primi come parametri di query .
I miei risultati indicano che non è necessario preoccuparsi di sovraccaricare l'ottimizzatore del piano di query o di ottenere piani senza utilizzo dell'indice , poiché trasformerà la query in modo = ANY({...}::integer[])
che possa utilizzare gli indici come previsto:
-- 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);
Tuttavia, questo (piuttosto vecchio) thread sulla mailing list di pgsql-hackers indica che c'è ancora un costo non trascurabile nella pianificazione di tali query, quindi prendi la mia parola con un pizzico di sale.
Se hai query come:
SELECT * FROM user WHERE id IN (1, 2, 3, 4 -- and thousands of another keys)
puoi aumentare le prestazioni se riscrivi la tua query come:
SELECT * FROM user WHERE id = ANY(VALUES (1), (2), (3), (4) -- and thousands of another keys)
EXPLAIN
dice che è internamente riscrivere la mia IN (...)
come ANY ('{...}'::integer[])
.
Potresti prendere in considerazione il refactoring di quella query invece di aggiungere un elenco arbitrariamente lungo di ID ... Potresti usare un intervallo se gli ID effettivamente seguono il modello nel tuo esempio:
SELECT * FROM user WHERE id >= minValue AND id <= maxValue;
Un'altra opzione è quella di aggiungere una selezione interna:
SELECT *
FROM user
WHERE id IN (
SELECT userId
FROM ForumThreads ft
WHERE ft.id = X
);