有没有办法反转 ActiveRecord::Relation 查询?
Posted
技术标签:
【中文标题】有没有办法反转 ActiveRecord::Relation 查询?【英文标题】:Is there a way to invert an ActiveRecord::Relation query? 【发布时间】:2011-07-15 05:20:09 【问题描述】:假设我们有以下内容:
irb> Post.where(:hidden => true).to_sql
=> "SELECT `posts`.* FROM `posts` WHERE posts.hidden = 1"
我们能否以某种方式从中获得反向 SQL 查询?
我要找的应该是这样的:
irb> Post.where(:hidden => true).invert.to_sql
=> "SELECT `posts`.* FROM `posts` WHERE NOT (posts.hidden = 1)"
【问题讨论】:
嗯。有人对where(:hidden => false)
的评论投了不好的票。该代码将不会生成 OP 正在寻找的 SQL 类型。
【参考方案1】:
invert_where (Rails 7+)
从 Rails 7 开始,有一个新的invert_where 方法。
根据文档,它:
允许您反转整个 where 子句,而不是手动应用条件。
class User
scope :active, -> where(accepted: true, locked: false)
end
User.where(accepted: true)
# WHERE `accepted` = 1
User.where(accepted: true).invert_where
# WHERE `accepted` != 1
User.active
# WHERE `accepted` = 1 AND `locked` = 0
User.active.invert_where
# WHERE NOT (`accepted` = 1 AND `locked` = 0)
要小心,因为这会在调用 invert_where 之前反转所有条件。
class User
scope :active, -> where(accepted: true, locked: false)
scope :inactive, -> active.invert_where # Do not attempt it
end
# It also inverts `where(role: 'admin')` unexpectedly.
User.where(role: 'admin').inactive
# WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)
来源:
invert_where 来自官方 Rails API 文档。 Link to the PR。 BigBinary blog article about invert_where method。【讨论】:
他,我刚刚在谷歌上搜索了一个反向的解决方案,你昨天回答了这个解决方案。迁移到 rails 7 的另一个原因 :)【参考方案2】:终于有了 Rails 7 的 invert_where
方法。
irb> Post.where(:hidden => true).invert_where.to_sql
"SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"hidden\" != 1"
请查看commit reference了解更多详情。
【讨论】:
【参考方案3】:在 rails 4 中有 not
用于此目的的后缀:
Post.where.not(hidden: true).to_sql
# => SELECT FROM `posts` WHERE `posts`.`hidden` != 1
在 Rails 3 中,您可以使用 squeel gem。它提供了许多有用的功能。有了它,你可以写:
Post.where hidden != true .to_sql
# => SELECT FROM `posts` WHERE `posts`.`hidden` != 1
【讨论】:
【参考方案4】:我们可以通过将反向查询传回 ActiveRecord 来进一步处理 Zabba's answer:
table = Post.arel_table
query = table[:hidden].eq(true).not # the inverted query, still ARel
Post.where(query) # plug it back into ActiveRecord
这将返回 ActiveRecord 对象,正如您通常所期望的那样。
【讨论】:
【参考方案5】:当我查找具有“不真实”条件(例如,假或无)的记录时,我会做的是:
Post.where(["(hidden IS NULL) OR (hidden = ?)", false])
【讨论】:
【参考方案6】:使用不同的语法,是的。示例:
posts = Post.scoped.table # or Arel::Table.new("posts")
posts.where(posts[:hidden].eq(true).not).to_sql
# => SELECT FROM `posts` WHERE NOT ((`posts`.`hidden` = 1))
【讨论】:
不是我想要的,但仍然是一个很好的答案。 您找到其他方法了吗?也许更好的语法?答案对你有用吗? 实际上我认为这甚至是不可能的,因为我实际上要求的是反转和 ActiveRecord::Relation 对象,它可能有也可能没有多个连接,并且包含会使事情变得过于复杂(其中 WHEREs我们应该倒置,哪个不倒置?)。我想我会保持开放状态,直到出现答案。以上是关于有没有办法反转 ActiveRecord::Relation 查询?的主要内容,如果未能解决你的问题,请参考以下文章