postgresql 如何在WHERE子句中使用ANY代替IN?

8ljdwjyq  于 2023-01-05  发布在  PostgreSQL
关注(0)|答案(1)|浏览(408)

我曾经有一个类似于Rails的查询:

MyModel.where(id: ids)

它生成如下SQL查询:

SELECT "my_models".* FROM "my_models"
WHERE  "my_models"."id" IN (1, 28, 7, 8, 12)

现在我想将其更改为使用ANY而不是IN

MyModel.where("id = ANY(VALUES(#{ids.join '),('}))"

现在,当我使用空数组ids = []时,我得到以下错误:

MyModel Load (53.0ms)  SELECT "my_models".* FROM "my_models"  WHERE (id = ANY(VALUES()))
ActiveRecord::JDBCError: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
Position: 75: SELECT "social_messages".* FROM "social_messages"  WHERE (id = ANY(VALUES()))
    from arjdbc/jdbc/RubyJdbcConnection.java:838:in `execute_query'
0g0grzrc

0g0grzrc1#

IN表达式有两种变体:

  • expression IN (subquery)
  • expression IN (value [, ...])

类似地,具有ANY结构的两个变体:

  • expression operator ANY (subquery)
  • expression operator ANY (array expression)

子查询适用于这两种技术,但是对于每种技术的第二种形式,IN需要一个值列表(如标准SQL中定义的),而= ANY需要一个数组

使用哪一个?

ANY是一个后来的、更通用的加法运算,它可以与任何返回布尔值的二元运算符组合使用。IN被简化为ANY的一个特例。事实上,它的第二种形式是在内部重写的:
使用= ANY重写IN
使用<> ALL重写NOT IN
检查EXPLAIN输出中的任何查询,以亲自查看。

  • IN永远不会比= ANY快。
  • = ANY不会有实质性的提高。

选择应取决于哪个更容易提供:值列表或数组(可能作为数组文字-单个值)。
如果您要传递的ID来自DB内部,直接选择它们(子查询)或使用JOIN(如@mu注解)将源表集成到查询中会更有效。
要从客户端传递一个***长列表*并获得最佳*性能***,请使用数组unnest()和join,或使用VALUES将其作为表表达式提供(就像@PinnyM评论的那样)。但是注意,JOIN在提供的数组/集合中保留了可能的重复项,而IN= ANY则没有。更多信息:

如果存在NULL值,NOT IN通常是错误的选择,而NOT EXISTS是正确的(而且速度更快):

  • 选择其他表中不存在的行

= ANY的语法

对于Postgres接受的数组表达式:

  • 一个数组构造函数(数组是从Postgres端的值列表构造的),格式为:ARRAY[1,2,3]
  • '{1,2,3}'形式的数组文字量

若要避免无效的类型转换,可以显式转换:

ARRAY[1,2,3]::numeric[]
'{1,2,3}'::bigint[]

相关:

  • PostgreSQL:将数组传递到过程时出现问题
  • 如何将自定义类型数组传递给Postgres函数

或者,你可以创建一个带VARIADIC参数的Postgres函数,该函数接受单个参数并将它们组成一个数组:

  • 在单个参数中传递多个值

如何从Ruby传递数组?

假设id等于integer

MyModel.where('id = ANY(ARRAY[?]::int[])', ids.map { |i| i})

但我只是在涉猎Ruby。@mu在这个相关的答案中提供了详细的说明:

  • 用ruby向sql查询发送值数组?

相关问题