Di Postgres, Anda dapat menentukan klausa IN, seperti ini:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
Adakah yang tahu berapa jumlah maksimum parameter yang dapat Anda berikan ke IN?
Di Postgres, Anda dapat menentukan klausa IN, seperti ini:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
Adakah yang tahu berapa jumlah maksimum parameter yang dapat Anda berikan ke IN?
Jawaban:
Menurut kode sumber yang terletak di sini, mulai dari baris 850, PostgreSQL tidak secara eksplisit membatasi jumlah argumen.
Berikut ini adalah komentar kode dari baris 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.
*/
Ini sebenarnya bukan jawaban untuk pertanyaan ini, tetapi mungkin juga membantu orang lain.
Setidaknya saya bisa tahu ada batas teknis 32767 nilai (= Short.MAX_VALUE) yang dapat dilewati untuk backend PostgreSQL, menggunakan driver JDBC Posgresql's 9.1.
Ini adalah tes "delete from x where id in (... 100k values ...)" dengan 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[]))
Tetapi jika coba kueri ke-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)
Kita dapat melihat bahwa postgres membuat tabel temp dan bergabung dengannya
Tidak ada batasan jumlah elemen yang Anda lewati untuk klausa IN. Jika ada lebih banyak elemen, ia akan menganggapnya sebagai array dan kemudian untuk setiap pemindaian dalam database, ia akan memeriksa apakah terdapat dalam array atau tidak. Pendekatan ini tidak begitu scalable. Alih-alih menggunakan klausa IN coba gunakan INNER JOIN dengan temp table. Lihat http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/ untuk info lebih lanjut. Menggunakan skala INNER JOIN serta pengoptimal kueri dapat menggunakan hash join dan optimisasi lainnya. Sedangkan dengan klausa IN tidak ada cara bagi pengoptimal untuk mengoptimalkan kueri. Saya perhatikan speedup minimal 2x dengan perubahan ini.
ORdan INklausa karena besarnya overhead dalam penguraian dan perencanaan kueri seperti itu, saya tidak dapat mengkonfirmasi masalah dengan Postgres 9.5, lihat jawaban ini .
Sebagai seseorang yang lebih berpengalaman dengan Oracle DB, saya juga khawatir tentang batasan ini. Saya melakukan tes kinerja untuk kueri dengan ~ 10'000 parameter dalam daftar IN, mengambil bilangan prima hingga 100'000 dari tabel dengan bilangan bulat 100'000 pertama dengan benar-benar mendaftarkan semua bilangan prima sebagai parameter kueri .
Hasil saya menunjukkan bahwa Anda tidak perlu khawatir tentang kelebihan pengoptimal rencana kueri atau mendapatkan rencana tanpa penggunaan indeks , karena itu akan mengubah kueri untuk digunakan di = ANY({...}::integer[])mana ia dapat meningkatkan indeks seperti yang diharapkan:
-- 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);
Namun, utas ini (yang agak lama) pada milis pgsql-hacker menunjukkan bahwa masih ada biaya yang tidak dapat diabaikan dalam perencanaan pertanyaan semacam itu, jadi terima kata-kata saya dengan sebutir garam.
Jika Anda memiliki pertanyaan seperti:
SELECT * FROM user WHERE id IN (1, 2, 3, 4 -- and thousands of another keys)
Anda dapat meningkatkan kinerja jika menulis ulang kueri Anda seperti:
SELECT * FROM user WHERE id = ANY(VALUES (1), (2), (3), (4) -- and thousands of another keys)
EXPLAINmengatakan secara internal menulis ulang IN (...)as saya ANY ('{...}'::integer[]).
Anda mungkin ingin mempertimbangkan refactoring kueri itu alih-alih menambahkan daftar id panjang yang sewenang-wenang ... Anda bisa menggunakan rentang jika id memang mengikuti pola dalam contoh Anda:
SELECT * FROM user WHERE id >= minValue AND id <= maxValue;
Pilihan lain adalah menambahkan pilih dalam:
SELECT *
FROM user
WHERE id IN (
SELECT userId
FROM ForumThreads ft
WHERE ft.id = X
);