Postgres'de, aşağıdaki gibi bir IN yan tümcesi belirtebilirsiniz:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
IN'ye aktarabileceğiniz maksimum parametre sayısının ne olduğunu bilen var mı?
Postgres'de, aşağıdaki gibi bir IN yan tümcesi belirtebilirsiniz:
SELECT * FROM user WHERE id IN (1000, 1001, 1002)
IN'ye aktarabileceğiniz maksimum parametre sayısının ne olduğunu bilen var mı?
Yanıtlar:
Burada bulunan kaynak koduna göre , 850 satırından başlayarak PostgreSQL, bağımsız değişkenlerin sayısını açıkça sınırlamaz.
Aşağıda, 870 satırından bir kod yorumu yer almaktadır:
/*
* 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.
*/
Bu gerçekten mevcut sorunun cevabı değil, başkalarına da yardımcı olabilir.
En azından Posgresql JDBC sürücüsü 9.1 kullanarak PostgreSQL arka ucuna geçebilen 32767 değerin (= Kısa.MAX_VALUE) teknik bir sınırı olduğunu söyleyebilirim.
Bu "postgresql jdbc sürücüsü ile id in (... 100k değerleri ...)" olan x'ten sil:
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[]))
Ancak 2. sorguyu deneyin:
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'in geçici tablo oluşturduğunu ve onunla birleştiğini görebiliriz
IN yan tümcesine ilettiğiniz öğelerin sayısında bir sınırlama yoktur. Daha fazla öğe varsa, bunu dizi olarak kabul eder ve daha sonra veritabanındaki her tarama için dizinin içinde olup olmadığını kontrol eder. Bu yaklaşım çok ölçeklenebilir değildir. IN yan tümcesini kullanmak yerine geçici tablo ile INNER JOIN kullanmayı deneyin. Daha fazla bilgi için http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/ adresine bakın . INNER JOIN ölçeklerinin yanı sıra sorgu optimizer'ı kullanmak karma birleştirme ve diğer optimizasyonları kullanabilir. Oysa IN yan tümcesinde, iyileştiricinin sorguyu optimize etmesinin bir yolu yoktur. Bu değişiklikle en az 2 kat hızlandığını fark ettim.
ORve INyan tümceleri kullanma üzerinde büyük bir performans artışı sağladığını doğrulayabilsem de, Postgres 9.5 ile ilgili sorunu doğrulayamadım, bu cevaba bakın .
Oracle DB ile daha deneyimli biri olarak ben de bu sınırdan endişeliydim. Bir INlistede ~ 10'000 parametresi olan bir sorgu için performans testi gerçekleştirdim , tüm asal sayıları sorgu parametreleri olarak listeleyerek ilk 100'000 tamsayı içeren bir tablodan 100'000'e kadar asal sayıları getirdim .
Sonuçlarım , sorgu planı optimize edicisini aşırı yükleme veya dizin kullanımı olmadan plan alma konusunda endişelenmeniz gerekmediğini gösteriyor , çünkü sorguyu = ANY({...}::integer[])beklendiği gibi endekslerden yararlanabileceği yere dönüştürecek :
-- 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);
Ancak, pgsql-hackerların posta listesindeki bu (oldukça eski) iş parçacığı, bu tür sorguların planlanmasında hala ihmal edilemez bir maliyet olduğunu gösterir, bu yüzden sözümü bir tuz tanesi ile alın.
Şunun gibi bir sorunuz varsa:
SELECT * FROM user WHERE id IN (1, 2, 3, 4 -- and thousands of another keys)
sorgunuzu aşağıdaki gibi yeniden yazarsanız performansı artırabilirsiniz:
SELECT * FROM user WHERE id = ANY(VALUES (1), (2), (3), (4) -- and thousands of another keys)
EXPLAINdahili IN (...)olarak benim şeklimi yeniden yazdığını söylüyor ANY ('{...}'::integer[]).
İsteğe bağlı olarak uzun bir kimlik listesi eklemek yerine bu sorguyu yeniden düzenlemeyi düşünebilirsiniz ... Kimlikler, örneğinizdeki deseni gerçekten izliyorsa bir aralık kullanabilirsiniz:
SELECT * FROM user WHERE id >= minValue AND id <= maxValue;
Başka bir seçenek de bir iç seçim eklemektir:
SELECT *
FROM user
WHERE id IN (
SELECT userId
FROM ForumThreads ft
WHERE ft.id = X
);