【问题标题】:How to use ANY instead of IN in a WHERE clause with Rails?如何在 Rails 的 WHERE 子句中使用 ANY 而不是 IN?
【发布时间】:2023-03-05 02:01:01
【问题描述】:

我曾经有过这样的查询:

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'

【问题讨论】:

  • 如果您要编写自定义查询,请非常小心使用占位符:VALUES(?) 根据需要扩展并绑定数组比您使用的要好得多有这里。您需要小心正确地转义注入 SQL 的任何原始值。这个查询的目的是什么?
  • 在我的真实查询中,我在 IN 部分有很多 ID,所以我想在这种情况下使用 ANY 来优化它。
  • 在 PostgreSQL 中,IN= ANY 的别名
  • 这些 id 是从哪里来的?也许您应该加入或使用子查询,而不是向数据库发送大量 id。
  • @PinnyM: IN 不是 = ANY 的别名。我提供了详细信息。

标签: sql ruby-on-rails-3 postgresql ruby-on-rails-4 activerecord


【解决方案1】:

IN 表达式有两种变体:

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

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

使用哪个?

ANY 是后来更通用的加法,它可以与任何返回布尔值的二元运算符结合使用。 IN 烧毁为ANY 的特例。事实上,它的第二种形式是在内部重写的:

IN 改写为= ANY
NOT IN 改写为<> ALL

检查EXPLAIN 输出中的任何查询以亲自查看。这证明了两件事:

  • IN 永远不会比 = ANY 快。
  • = ANY 不会快很多。

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

如果您要传递的 ID 无论如何都来自数据库中,那么直接选择它们(子查询)或使用 JOIN 将源表集成到查询中会更有效(比如@mu commented)。

要从您的客户端传递长列表的值并获得最佳性能,请使用数组, unnest() 并加入,或使用VALUES 将其作为表表达式提供(如@PinnyM commented)。但请注意,JOIN 会在提供的数组/集合中保留可能的重复项,而IN= ANY 则不会。更多:

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

= ANY 的语法

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

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

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

相关:

或者您可以创建一个采用VARIADIC 参数的Postgres 函数,该函数采用单个参数并从中形成一个数组:

如何从 Ruby 中传递数组?

假设idinteger

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

但我只是涉足 Ruby。 @mu 在此相关答案中提供了详细说明:

【讨论】:

  • 谢谢,这好多了:) 我将如何在 rails 和 activemodel 中实现它?谢谢
  • 您的解决方案工作完美,除非我使用空数组我得到: ActiveRecord::StatementInvalid (ActiveRecord::JDBCError: org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = text Hint : 没有运算符与给定的名称和参数类型匹配。您可能需要添加显式类型转换。
  • @EkiEqbal 如 Erwin 所述,在数组后添加 ::numeric[] 以避免此问题。
  • @EkiEqbal: ... 或 ::int[] 在这种情况下。
  • @ErwinBrandstetter 考虑到 OP 试图通过使用值列表而不是数组来提高性能(假设 the linked article in above comments 仍然正确),答案应该使用格式 VALUES (...), (...), ... 而不是 @ 987654374@。还是我错过了什么?如果文章不再正确(或从未正确),那么这个练习似乎没有意义......
猜你喜欢
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 2011-06-30
  • 1970-01-01
相关资源
最近更新 更多