假设我们有以下内容:
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)"
答案 0 :(得分:17)
使用不同的语法,是的。例如:
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))
答案 1 :(得分:6)
在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
答案 2 :(得分:5)
我们可以通过将反向查询传递回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对象,正如您通常所期望的那样。
答案 3 :(得分:1)
从 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)
来源:
答案 4 :(得分:0)
当我正在寻找具有“不真实”条件(例如,假或零)的记录时,我所做的是:
Post.where(["(hidden IS NULL) OR (hidden = ?)", false])
答案 5 :(得分:0)
终于有了 Rails 7 的 invert_where
方法。
irb> Post.where(:hidden => true).invert_where.to_sql
"SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"hidden\" != 1"
有关详细信息,请查看此 commit reference。
答案 6 :(得分:-1)
对于单表查询,这将起作用:
query = Post.where(hidden:true)
inverse = Post.where('id not in (?)', query.pluck(:id))
当然,我们遇到了可怕的效率问题,并且它不会生成您要求的SQL查询(尽管在运行时,它最终会生成相同的结果集)。