如何在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
。 我创build了这个:
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式有两种变体:
-
expression IN (subquery)
-
expression IN (value [, ...])
同样, ANY
构造有两个变体:
-
expression operator ANY (subquery)
-
expression operator ANY (array expression)
一个子查询适用于任何一种技术,但是对于每种技术的第二种forms, IN
需要一个值列表 (在标准SQL中定义),而= ANY
需要一个数组 。
使用哪个?
ANY
是一个更新,更通用的补充,它可以与任何返回布尔值的二元运算符结合使用。 IN
烧伤了ANY
一个特殊情况。 事实上,它的第二种forms是内部改写的:
IN
被改写为= ANY
NOT IN
用<> ALL
重写
检查EXPLAIN
输出以查看任何查询。 这certificate了两件事:
-
IN
永远不会比= ANY
快。 -
= ANY
不会快得多。
select应该由容易提供的内容决定:值列表或数组(可能是数组字面值 – 单个值)。
如果要传递的ID 来自数据库 ,则直接select它们(子查询)或将源表与JOIN
(如@mu注释 )集成到查询中效率更高 。
要从客户端传递一长串值并获得最佳性能 ,请使用数组unnest()
和join,或者使用VALUES
(如@PinnyM注释 )将其作为表expression式提供。 更多:
- 用一个大的IN优化一个Postgres查询
在存在NULL值的情况下, NOT IN
通常是错误的select, NOT EXISTS
将是正确的(也是更快的):
- select其他表中不存在的行
语法for = ANY
对于Postgres接受的数组expression式:
- 一个数组构造函数 (数组是从Postgres一侧的值列表构造的):
ARRAY[1,2,3]
- 或forms为
'{1,2,3}'
的数组字面值 。
为了避免无效的types转换,你可以明确地强制转换:
ARRAY[1,2,3]::numeric[] '{1,2,3}'::bigint[]
有关:
- PostgreSQL:将数组传递给过程的问题
- 如何将自定义types数组传递给Postgres函数
或者你可以创build一个采用VARIADIC
参数的Postgres函数,该参数需要单独的参数并从中形成一个数组:
- 在单个参数中传递多个值
如何从Ruby传递数组?
假设id
是integer
:
MyModel.where('id = ANY(ARRAY[?]::int[])', ids.map { |i| i})
但是我只是在讨论Ruby。 @mu在这个相关的答案中提供了详细的说明:
- 发送值的数组到Ruby中的SQL查询?