PostgreSQL - จำนวนพารามิเตอร์สูงสุดในข้อ“ IN”?


147

ใน Postgres คุณสามารถระบุประโยค IN ดังนี้:

SELECT * FROM user WHERE id IN (1000, 1001, 1002)

มีใครรู้บ้างว่าจำนวนพารามิเตอร์สูงสุดที่คุณสามารถส่งผ่านเข้าสู่ IN คืออะไร

คำตอบ:


83

ตามซอร์สโค้ดที่อยู่ที่นี่เริ่มต้นที่บรรทัดที่ 850 PostgreSQL ไม่ได้ จำกัด จำนวนอาร์กิวเมนต์อย่างชัดเจน

ต่อไปนี้เป็นความคิดเห็นรหัสจากบรรทัด 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.
 */

56

นี่ไม่ใช่คำตอบสำหรับคำถามปัจจุบัน แต่อาจช่วยผู้อื่นได้เช่นกัน

อย่างน้อยฉันสามารถบอกได้ว่ามีขีด จำกัด ทางเทคนิคของค่า 32767 (= Short.MAX_VALUE) ที่ส่งผ่านไปยังแบ็กเอนด์ PostgreSQL โดยใช้ไดร์เวอร์ JDBC ของ Posgresql 9.1

นี่คือการทดสอบ "ลบจาก x โดยที่ id ใน (... ค่า 100k ... )" ด้วยไดรเวอร์ 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)

6
OP ได้ถามเกี่ยวกับข้อ จำกัด ของเอ็นจิน DB แต่การค้นหาข้อ จำกัด ของ JDBC ฉันมาที่นี่และนั่นคือสิ่งที่ฉันชอบ อย่างไรก็ตามมีข้อ จำกัด ค่อนข้างสูง
9ilsdx 9rvj 0lo

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

แผน QUERY

 Seq Scan on test  (cost=0.00..1.38 rows=2 width=208)
   Filter: (id = ANY ('{1,2}'::bigint[]))

แต่ถ้าลองเคียวรี่ที่ 2:

explain select * from test where id = any (values (1), (2));

แผน QUERY

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 สร้างตารางชั่วคราวและเข้าร่วมกับมัน


แต่สิ่งที่ฉันได้ยินมาว่า postgres-9.3 + ทั้งคู่ดูเหมือนจะเป็นนักแสดงคนเดียวกัน datadoghq.com/blog/…
PiyusG

18

ไม่มีการ จำกัด จำนวนองค์ประกอบที่คุณส่งไปยังในข้อ หากมีองค์ประกอบเพิ่มเติมจะพิจารณาว่าเป็นอาร์เรย์และจากนั้นสำหรับการสแกนแต่ละครั้งในฐานข้อมูลจะตรวจสอบว่ามีอยู่ในอาร์เรย์หรือไม่ วิธีนี้ไม่สามารถปรับขนาดได้ แทนที่จะใช้ IN clause ให้ลองใช้ INNER JOIN กับตาราง temp ดูhttp://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/สำหรับข้อมูลเพิ่มเติม การใช้ INNER JOIN ลดขนาดเช่นเดียวกับเครื่องมือเพิ่มประสิทธิภาพการสืบค้นสามารถใช้การเข้าร่วมแฮชและการเพิ่มประสิทธิภาพอื่น ในขณะที่ส่วนคำสั่ง IN ไม่มีวิธีใดที่เครื่องมือเพิ่มประสิทธิภาพเพื่อปรับการสืบค้นให้เหมาะสม ฉันสังเกตเห็นการเร่งความเร็วอย่างน้อย 2x กับการเปลี่ยนแปลงนี้


2
ลิงค์ที่คุณอ้างถึงไม่ได้บอกว่า DBMS นั้นพูดถึงอะไร ในขณะที่ฉันสามารถยืนยันได้ว่าใน Oracle DB การใช้ตารางชั่วคราวจะช่วยเพิ่มประสิทธิภาพการใช้แบบสอบถามORและการรวมINคำสั่งเนื่องจากค่าใช้จ่ายจำนวนมากในการแยกวิเคราะห์และวางแผนแบบสอบถามดังกล่าว แต่ฉันไม่สามารถยืนยันปัญหากับ Postgres 9.5 ได้ดูคำตอบนี้
blubb

17

ในฐานะที่เป็นคนที่มีประสบการณ์กับ Oracle DB มากขึ้นฉันก็กังวลเกี่ยวกับขีด จำกัด นี้เช่นกัน ผมดำเนินการทดสอบประสิทธิภาพการทำงานสำหรับการค้นหาที่มีพารามิเตอร์ ~ 10'000 ในIN-list เอ็นดูตัวเลขที่สำคัญถึง 100'000 จากตารางที่มี 100'000 จำนวนเต็มแรกจริงโดยรายชื่อทั้งหมดที่ตัวเลขที่สำคัญเป็นพารามิเตอร์การค้นหา

ผลลัพธ์ของฉันระบุว่าคุณไม่จำเป็นต้องกังวลเกี่ยวกับเครื่องมือเพิ่มประสิทธิภาพแผนแบบสอบถามมากเกินไปหรือรับแผนโดยไม่มีการใช้ดัชนีเนื่องจากมันจะเปลี่ยนแบบสอบถามให้ใช้ใน= ANY({...}::integer[])กรณีที่สามารถใช้ดัชนีได้ตามที่คาดไว้:

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

อย่างไรก็ตามกระทู้ (ค่อนข้างเก่า) ในรายการส่งเมล pgsql-hackersบ่งชี้ว่ายังคงมีค่าใช้จ่ายที่ไม่สำคัญในการวางแผนข้อความค้นหาดังกล่าวดังนั้นโปรดใช้คำพูดของฉันด้วยเม็ดเกลือ


3

หากคุณมีคำถามเช่น:

SELECT * FROM user WHERE id IN (1, 2, 3, 4 -- and thousands of another keys)

คุณสามารถเพิ่ม performace ถ้าเขียนแบบสอบถามของคุณเช่น:

SELECT * FROM user WHERE id = ANY(VALUES (1), (2), (3), (4) -- and thousands of another keys)

10
ของ PostgreSQL EXPLAINบอกว่ามันเป็นภายในเขียนใหม่ของฉันเป็นIN (...) ANY ('{...}'::integer[])
Kiran Jonnalagadda

4
อย่างไรก็ตาม @KiranJonnalagadda จะเพิ่มประสิทธิภาพ (เล็กน้อยมาก) หากไม่จำเป็นต้องทำงานภายใน
Rodrigo


0

คุณอาจต้องการพิจารณาการเปลี่ยนโครงสร้างการสืบค้นใหม่แทนการเพิ่มรายการ id ที่มีความยาวโดยพลการ ... คุณสามารถใช้ช่วงได้ถ้ารหัสจริงตามรูปแบบในตัวอย่างของคุณ:

SELECT * FROM user WHERE id >= minValue AND id <= maxValue;

ตัวเลือกอื่นคือการเพิ่มตัวเลือกภายใน:

SELECT * 
FROM user 
WHERE id IN (
    SELECT userId
    FROM ForumThreads ft
    WHERE ft.id = X
);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.