PostgreSQL - “IN” yan tümcesinde maksimum parametre sayısı?


147

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:


83

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.
 */

56

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)

6
OP, DB motor sınırlaması hakkında sordu, ancak JDBC sınırlaması için arama yaptım Buraya geldim ve bunun için lloking yapıyordum. Ancak, oldukça yüksek bir sınırlama var.
9ilsdx 9rvj 0lo

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

QUERY PLAN

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

QUERY PLAN

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


Ama postgres-9.3 + her ikisinin de aynı performansta olduğunu duydum. datadoghq.com/blog/…
PiyusG

18

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.


2
Bahsettiğiniz bağlantı hangi DBMS'den bahsettiğini söylemiyor. Oracle DB'de, geçici tablolar kullanmanın , bu tür sorguların ayrıştırılması ve planlanmasındaki büyük ek yük nedeniyle sorguları birleştirme 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 .
blubb

17

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.


3

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

10
PostgreSQL'ler EXPLAINdahili IN (...)olarak benim şeklimi yeniden yazdığını söylüyor ANY ('{...}'::integer[]).
Kiran Jonnalagadda

4
Her neyse, @KiranJonnalagadda, dahili bir çalışmaya ihtiyaç duyulmuyorsa performansı (ihmal edilebilir, belki de) artırır.
Rodrigo

1

Sadece denedim. cevap, 2 baytlık bir değer olarak -> aralık dışı tam sayı: 32768


0

İ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
);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.