PostgreSQL-“ IN”子句中的最大参数数量?


147

在Postgres中,您可以指定一个IN子句,如下所示:

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

有谁知道您可以传入IN的最大参数数量是多少?

Answers:


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

这并不是对当前问题的真正答案,但是也可能对其他人有所帮助。

至少我可以说使用Posgresql的JDBC驱动程序9.1可传递到PostgreSQL后端的技术限制为32767个值(= Short.MAX_VALUE)。

这是使用postgresql jdbc驱动程序对“从x中的id删除(... 100k值...)中的ID”的测试:

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询问了有关数据库引擎限制的信息,但是搜索JDBC限制时我来到了这里,这就是我一直追求的目标。因此,有一个很大的限制。
9ilsdx 9rvj 0lo

36
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[]))

但是,如果尝试第二查询:

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建立了临时表并与之连接


但是我听说postgres-9.3 +似乎都是一样的性能。datadoghq.com/blog/…–
PiyusG

18

传递给IN子句的元素数量没有限制。如果有更多元素,它将视为数组,然后对于数据库中的每次扫描,将检查它是否包含在数组中。这种方法不是很可扩展。而不是使用IN子句,请尝试对临时表使用INNER JOIN。有关更多信息,请参见http://www.xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic/。很好地使用INNER JOIN伸缩,查询优化器可以利用哈希联接和其他优化。而使用IN子句,优化器无法优化查询。我注意到此更改使速度至少提高了2倍。


2
您所指向的链接没有说明它在谈论什么DBMS。尽管我可以确认在Oracle DB上,由于解析和计划此类查询会产生大量开销,所以使用临时表比使用查询合并ORIN子句可以显着提高性能,但是我无法确认Postgres 9.5的问题,请参见此答案
blubb

17

作为对Oracle DB更有经验的人,我也担心此限制。我对IN-list中具有约10,000个参数的查询进行了性能测试,通过实际列出所有质数作为查询参数,从具有前100000个整数的表中获取高达100000 的质数

我的结果表明,你不必有关超载的查询计划优化器或越来越不计划使用索引担心,因为这将改变查询中使用= 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)

如果像这样重写查询,则可以提高性能:

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

10
PostgreSQL EXPLAIN表示正在内部将my重写IN (...)ANY ('{...}'::integer[])
Kiran Jonnalagadda

4
无论如何,@ KiranJonnalagadda可以在不需要内部工作的情况下提高性能(也许可以忽略不计)。
罗德里戈

1

刚刚尝试过。答案是->作为2字节值的超出范围的整数:32768


0

您可能要考虑重构该查询,而不是添加任意长的ID列表...如果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
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.