在PostgreSQL中,IN子句可以使用多少个参数?

3

我正在从Java程序中执行PostgreSQL数据库查询,查询语句如下:

SELECT ... FROM table_name WHERE column_name IN("<list of values>");

问题在于值列表是动态创建的,其大小没有限制,当该列表变得太大时,我会收到一个带有消息“堆栈深度长度超过限制”的PSQLException异常。 我的问题是,在发生此异常之前,我如何确定可以使用多少参数来使用IN子句?假设max_stack_depth为2MB,我可以使用它来确定我可以在IN子句中使用多少参数吗?


2
你尝试过使用 column_name IN("<第一个值列表>") OR column_name IN("<第二个值列表>") 吗?它是否允许你传递更多的项?我曾经用这个技巧解决了 Oracle 中的一个问题。但我不确定在 Postgresql 中是否可行。 - Sergey Kalinichenko
即使来自其他表,这些值是否来自同一个数据库? - Serge Ballesta
1
请展示完整的异常文本,并从 SELECT version() 中显示您的PostgreSQL版本。 - Craig Ringer
亲爱的 Craig,这是 PostgreSQL 的版本:PostgreSQL 8.4.20 on x86_64-redhat-linux-gnu,由 GCC gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-4) 编译,64 位。 - Nawar Khoury
关于异常的部分:Caused by: org.postgresql.util.PSQLException: ERROR: stack depth limit exceeded at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1531) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1313) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:188) at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:452) at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:354) - Nawar Khoury
显示剩余2条评论
3个回答

4

values 表进行 内连接(inner join)

select ...
from
    table_name
    inner join
    (values
        (first_value), (second_value)
    ) s(column_name) using (column_name)
;

或者更容易构建成字符串,转换为一个未嵌套的数组。
select ...
from
    table_name
    inner join
    unnest(
        array[first_value, second_value]
    ) s(column_name) using (column_name)
;

1
一个 values 列表可能会更快。对于非常大的列表,您甚至可以创建一个临时表。我之前写过一些关于它的内容 - 请参见 https://dev59.com/dmQn5IYBdhLWcg3wLkkd#17038097 - Craig Ringer
这是一个绝妙的解决方案……我想不到……非常感谢 :) 虽然我仍然不知道 IN 子句的列表限制,但将我的查询更改为这种方式实际上比分成多个列表或插入临时表更好:) 我将使用这个解决方案……谢谢 ;) - Nawar Khoury

2
如果您使用的是7.4或更早版本,则取决于max_expr_depth设置(或者在较新版本中是max_stack_depth)。
您可以随时检查其值:
test=> SHOW max_expr_depth;

并更改

test=> SET max_expr_depth TO 10;

然后您可以对这个值进行操作。

在此处找到

默认值为10000,对于任何正常查询来说都足够高,但是如果需要,您可以将其提高。(但是如果您将其提高得太高,可能会因堆栈溢出而导致服务器崩溃。)

这并不是您问题的最佳解决方案,但我相信它回答了您的问题。


3
但如果你正在使用7.4或更早的版本,那么你很可能已经半路走向化石化了。 - Craig Ringer
这就是为什么我提到了 max_stack_depth 属性(替代 max_expr_depth)的原因。 - Kamil Kłys
非常感谢大家的帮助,我实际上增加了max_depth作为临时解决方案,但现在我正在尝试优化我的程序,所以我不需要那个增量...这就是为什么我想知道IN子句允许的最大参数数量...但真的很感谢,我很感激你们的帮助 :) - Nawar Khoury

1

当你担心 in 子句中可能有大量的值时,可以使用临时表。

你可以创建一个只有一列和该列上索引的临时表。在查询之前,将 in 子句的数据作为行放入临时表中。然后,使用以下构造之一执行查询:

SELECT * FROM table_name WHERE column_name in (SELECT column_name FROM temptable);
SELECT * FROM table_name a JOIN temptable b ON (a.column_name = b.column_name);
SELECT * FROM table_name a WHERE EXISTS (SELECT * FROM temptable t WHERE a.column_name=t.column_name);

并行运行的查询不会有问题:由一个会话写入临时表中的内容对任何其他会话都是不可见的。

这种方法的最大优点是,SQL解析器不必生成所有值。查询的结构保持不变,甚至可以使用预编译语句。这与索引的使用结合起来可以极大地提高性能。


创建索引的成本比顺序扫描要高,而且使用它的可能性很小。 - Clodoaldo Neto
@ClodoaldoNeto 如果表中有大量行且RAM很少,则可能会使用索引,并且可能会更快(使用合并连接,因为索引已经排序)。或者位图索引可能会有用。 - stefan.schwetschke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接