sql – 如何在带有Rails的WHERE子句中使用ANY而不是IN?

我曾经有过如下查询
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'

解决方法

IN表达式有两种变体:

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

同样,具有ANY构造的两个变体:

> expression operator ANY (subquery)
> expression operator ANY (array expression)

查询适用于任何一种技术,但对于每种技术的第二种形式,IN需要一个值列表(如标准sql中所定义),而= ANY需要一个数组.

哪个用?

ANY是一个更加通用的新增功能,可以与任何返回布尔值的二元运算符组合使用. IN归结为任何特殊情况.事实上,它的第二种形式在内部被重写:

IN用= ANY重写
NOT IN用<>重写所有

检查EXPLAIN输出以查找任何查询以便自己查看.这证明了两件事:

> IN永远不会超过= ANY.
> =任何事情都不会快得多.

选择应该由更容易提供的内容决定:值列表或数组(可能作为数组文字 – 单个值).

如果您要传递的ID仍然来自数据库中,则直接选择它们(子查询)或使用JOIN将源表集成到查询中(如@mu commented)则更有效.

要从客户端传递一长串值并获得最佳性能,请使用数组,unexst()和join,或使用VALUES(如@PinnyM commented)将其作为表表达式提供.更多:

> Optimizing a Postgres query with a large IN

在存在NULL值的情况下,NOT IN通常是错误的选择,NOT EXISTS是正确的(也更快):

> Select rows which are not present in other table

语法= ANY

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

>一个array constructor(数组由Postgres一侧的值列表构成)形式:ARRAY [1,2,3]
>或“{1,3}”形式的array literal.

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

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

有关:

> PostgreSQL: Issue with passing array to procedure
> How to pass custom type array to Postgres function

或者你可以使用VARIADIC参数创建一个Postgres函数,该参数接受各个参数并从中形成一个数组:

> Passing multiple values in single parameter

如何从Ruby传递数组?

假设id为整数:

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

但我只是涉足Ruby. @mu提供了相关答案的详细说明:

> Sending array of values to a sql query in ruby?

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 &#39;EastRiver&#39; 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...