当前位置: 首页 > 知识库问答 >
问题:

如何在WHERE子句中使用ANY而不是IN?

顾池暝
2023-03-14

我以前有一个类似于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 '),('}))"

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

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'

共有1个答案

朱俭
2023-03-14

IN表达式有两种变体:

  • 表达式IN(子查询)
  • 表达式IN(值[,...])

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

  • 表达式运算符ANY(子查询)
  • 表达式操作符ANY(数组表达式)

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

ANY是一个更新的、更通用的添加,它可以与任何返回布尔值的二进制运算符结合使用。IN可归结为ANY的特例。事实上,它的第二种形式是在内部重写的:

中的被重写为=任何
不在中的被重写为

检查EXPLAIN(解释)输出中的任何查询,以便自己查看。这证明了两件事:

  • IN永远不会比=ANY更快。
  • =ANY不会更快。

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

如果您要传递的ID无论如何都来自DB内部,那么直接选择它们(子查询)或使用连接将源表集成到查询中(如@mu注释)会更加有效。

要从客户端传递一长串值并获得最佳性能,请使用数组、unnest()和join,或者使用值将其作为表表达式提供(如@PinnyM注释)。但请注意,联接会在提供的数组/集中保留可能的重复项,而在=任何中则不会。更多信息:

  • 使用大IN优化Postgres查询

在存在空值的情况下,不在通常是错误的选择,而不存在则是正确的(而且速度更快):

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

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

  • 数组构造函数(数组由Postgres端的值列表构造),其形式为:数组[1,2,3]
  • 或形式为“{1,2,3}”的数组文本

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

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

相关:

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

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

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

假设id为整数:

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

但我只是在尝试Ruby@mu在相关回答中提供了详细说明:

  • 在ruby中向sql查询发送值数组
 类似资料:
  • 问题内容: 在JPA中,查询为: 如何在JPA中将类别设置为任何类别?因此,如果传递了null类别,我将简单地忽略category参数,选择所有产品。 问题答案: 如何在JPA中将类别设置为任何类别?因此,如果传递了null类别,我将简单地忽略category参数,选择所有产品。 您必须在此处动态构建查询。使用HQL(这是一个简化的示例): 但是,实际上,我的建议是在此处使用Criteria AP

  • 问题内容: 我正在尝试搜索文本和备注的多列,以查找某些我不想看到的特定短语和黑名单短语。 假设下表 前任。我想找到所有提到(在任何领域中)“苹果”但黑名单中的“苹果酱”的故事。 如何在where子句中使用别名?我找不到有关此主题的任何文档: 1)这种方法可行吗? 2)替代方法是否意味着我将在每次行迭代中执行多个字符串连接? 问题答案: 我不能在where子句中使用别名。 这种方法可行吗? 当然,将

  • 问题内容: 为什么不能在where子句中使用临时列? 例如,此查询: 这将显示两列,一列称为,另一列称为。是即时创建的,始终为1或0,具体取决于是否有50个或该品牌的产品。 这一切对我来说都是有意义的,除了我不能仅在以下查询中选择时: 这给了我这个错误: 问题答案: 使用来代替: 被评估 前 的。被评估之后。

  • 问题内容: 在WHERE子句中有使用SELECT语句描述的名称吗?这是好/不好的做法吗? 这会是更好的选择吗? 它远没有那么优雅,但是运行起来比以前的版本要快。我不喜欢它,因为它在GUI中没有非常清晰地显示(并且SQL初学者需要理解它)。我可以将其分为两个独立的查询,但是随后事情变得混乱了…… 注意:我不仅需要日期和分数(例如姓名) 问题答案: 称为相关子查询。它有它的用途。

  • 问题内容: 我想在Derby数据库中模拟以下类型的查询(即Microsoft SQL Server语法)的效果。目标是返回表中存储在“ someColumn”中的日期少于7天的所有记录。这是我希望实现的Microsoft SQL语法… 我已经能够确定在Derby中它将涉及使用timestampdiff函数。但是我不知道Derby中WHERE子句中函数用法的语法,因此我找不到任何示例。我发现了很多在

  • 问题内容: 假设我有这张桌子 我想返回第一行,其中所有先前现金的总和大于某个值: 因此,例如,如果我要返回第一行,其中所有先前现金的总和都大于500,则应返回到第3行 如何使用mysql语句执行此操作? 使用 不起作用 问题答案: 您只能在HAVING子句中使用聚合进行比较: 该子句要求您定义GROUP BY子句。 要获得第一行,其中所有先前现金的总和都大于某个值,请使用: 由于聚合函数发生在子查