In Postgres können Sie eine IN-Klausel wie folgt angeben:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
Weiß jemand, wie viele Parameter Sie maximal an IN übergeben können?
In Postgres können Sie eine IN-Klausel wie folgt angeben:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
Weiß jemand, wie viele Parameter Sie maximal an IN übergeben können?
Antworten:
Gemäß dem hier befindlichen Quellcode , der in Zeile 850 beginnt, begrenzt PostgreSQL die Anzahl der Argumente nicht explizit.
Das Folgende ist ein Codekommentar aus Zeile 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.
*/
Dies ist keine wirkliche Antwort auf die vorliegende Frage, könnte aber auch anderen helfen.
Zumindest kann ich feststellen, dass es eine technische Grenze von 32767 Werten (= Short.MAX_VALUE) gibt, die mit dem JDBC-Treiber 9.1 von Posgresql an das PostgreSQL-Backend übergeben werden können.
Dies ist ein Test von "Aus x löschen, wobei ID in (... 100k-Werte ...)" mit dem Postgresql-JDBC-Treiber:
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[]))
Aber wenn Sie versuchen, 2. Abfrage:
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)
Wir können sehen, dass Postgres eine temporäre Tabelle erstellen und sich dieser anschließen
Die Anzahl der Elemente, die Sie an die IN-Klausel übergeben, ist unbegrenzt. Wenn mehr Elemente vorhanden sind, wird es als Array betrachtet und bei jedem Scan in der Datenbank wird geprüft, ob es im Array enthalten ist oder nicht. Dieser Ansatz ist nicht so skalierbar. Verwenden Sie anstelle der IN-Klausel INNER JOIN mit der temporären Tabelle. Weitere Informationen finden Sie unter http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/ . Wenn Sie die INNER JOIN-Skalierung gut verwenden, kann der Abfrageoptimierer Hash-Join und andere Optimierungen verwenden. Während mit der IN-Klausel für den Optimierer keine Möglichkeit besteht, die Abfrage zu optimieren. Ich habe mit dieser Änderung eine Beschleunigung von mindestens 2x festgestellt.
OR
und IN
-klauseln bedeutet, konnte ich das Problem mit Postgres 9.5 nicht bestätigen. Siehe diese Antwort .
Als jemand, der mehr Erfahrung mit Oracle DB hat, war ich auch über diese Grenze besorgt. Ich habe einen Leistungstest für eine Abfrage mit ~ 10'000 Parametern in einer IN
Liste durchgeführt und dabei Primzahlen bis zu 100'000 aus einer Tabelle mit den ersten 100'000 Ganzzahlen abgerufen, indem ich tatsächlich alle Primzahlen als Abfrageparameter aufgelistet habe .
Meine Ergebnisse zeigen, dass Sie sich keine Gedanken über das Überladen des Abfrageplanoptimierers oder das Abrufen von Plänen ohne Indexverwendung machen müssen , da die Abfrage so umgewandelt wird, dass = ANY({...}::integer[])
sie wie erwartet Indizes nutzen kann:
-- 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);
Dieser (ziemlich alte) Thread auf der Mailingliste von pgsql-hackers weist jedoch darauf hin, dass die Planung solcher Abfragen immer noch nicht zu vernachlässigende Kosten verursacht. Nehmen Sie also mein Wort mit einem Körnchen Salz.
Wenn Sie Fragen haben wie:
SELECT * FROM user WHERE id IN (1, 2, 3, 4 -- and thousands of another keys)
Sie können die Leistung erhöhen, wenn Sie Ihre Abfrage wie folgt umschreiben:
SELECT * FROM user WHERE id = ANY(VALUES (1), (2), (3), (4) -- and thousands of another keys)
EXPLAIN
sagt, dass es intern mein IN (...)
als neu schreibt ANY ('{...}'::integer[])
.
Möglicherweise möchten Sie diese Abfrage umgestalten, anstatt eine beliebig lange Liste von IDs hinzuzufügen. Sie können einen Bereich verwenden, wenn die IDs tatsächlich dem Muster in Ihrem Beispiel folgen:
SELECT * FROM user WHERE id >= minValue AND id <= maxValue;
Eine weitere Option ist das Hinzufügen einer inneren Auswahl:
SELECT *
FROM user
WHERE id IN (
SELECT userId
FROM ForumThreads ft
WHERE ft.id = X
);