Rails where 条件使用 NOT NIL

Posted

技术标签:

【中文标题】Rails where 条件使用 NOT NIL【英文标题】:Rails where condition using NOT NIL 【发布时间】:2011-05-14 05:36:28 【问题描述】:

使用 rails 3 风格我将如何写出相反的:

Foo.includes(:bar).where(:bars=>:id=>nil)

我想找到 id 不是 nil 的地方。我试过了:

Foo.includes(:bar).where(:bars=>:id=>!nil).to_sql

但是返回:

=> "SELECT     \"foos\".* FROM       \"foos\"  WHERE  (\"bars\".\"id\" = 1)"

这绝对不是我需要的,而且几乎看起来像是 ARel 中的一个错误。

【问题讨论】:

!nil 在 Ruby 中计算为 true,而 ARel 在 SQL 查询中将 true 转换为 1。因此,生成的查询实际上就是您所要求的 - 这不是 ARel 错误。 【参考方案1】:

使用 Rails 3 执行此操作的规范方法:

Foo.includes(:bar).where("bars.id IS NOT NULL")

ActiveRecord 4.0 及更高版本添加了where.not,因此您可以这样做:

Foo.includes(:bar).where.not('bars.id' => nil)
Foo.includes(:bar).where.not(bars:  id: nil )

在表之间使用范围时,我更喜欢利用merge,以便更轻松地使用现有范围。

Foo.includes(:bar).merge(Bar.where.not(id: nil))

另外,由于includes并不总是选择连接策略,所以这里也应该使用references,否则可能会得到无效的SQL。

Foo.includes(:bar)
   .references(:bar)
   .merge(Bar.where.not(id: nil))

【讨论】:

最后一个不适合我,我们需要额外的 gem 或插件吗?我得到:rails undefined method 'not_eq' for :confirmed_at:Symbol.. @Tim 是的,我在上面链接的 MetaWhere gem。 我喜欢不需要其他宝石的解决方案 :) 即使它有点难看 @oreoshake MetaWhere/Squeel 非常值得拥有,这只是一个小方面。当然,一般情况下最好知道。 @BKSpureon 链接 where 条件只是构建一个 AST,它不会访问数据库,直到您点击像 eachto_a 这样的终端方法。构建查询不是性能问题;你从数据库中请求的是什么。【参考方案2】:

这不是 ARel 中的错误,而是您的逻辑中的错误。

你想要的是:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))

【讨论】:

我很好奇将 !nil 变成 '1' 的逻辑是什么 猜测,!nil 返回true,这是一个布尔值。 :id => true 在 SQLese 中为您提供 id = 1 这是避免编写原始 sql 片段的好方法。虽然语法不如 Squeel 简洁。 我没有设法用 sqlite3 做到这一点。 sqlite3 想看field_name != 'NULL' @zetetic 除非你使用的是 postgres,否则你会得到 id = 't' :)【参考方案3】:

对于 Rails4:

所以,你想要的是一个内部连接,所以你真的应该只使用连接谓词:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

但是,为了记录,如果你想要一个“NOT NULL”条件,只需使用 not 谓词:

Foo.includes(:bar).where.not(bars: id: nil)

Select * from Foo Left Outer Join Bars on .. WHERE bars.id IS NOT NULL

注意,这个语法报告了一个弃用(它讲的是一个字符串 SQL sn-p,但我猜散列条件在解析器中被更改为字符串?),所以一定要在末尾添加引用:

Foo.includes(:bar).where.not(bars: id: nil).references(:bar)

弃用警告:您似乎急于加载表(一个 of: ....) 在字符串 SQL sn-p 中引用。例如:

Post.includes(:comments).where("comments.title = 'foo'")

目前,Active Record 识别字符串中的表,并且知道 将 cmets 表加入查询,而不是加载 cmets 在单独的查询中。然而,这样做没有写一个成熟的 SQL 解析器天生就有缺陷。因为我们不想写 SQL 解析器,我们正在删除此功能。从现在开始,你必须 当您从 字符串:

Post.includes(:comments).where("comments.title = 'foo'").references(:comments)

【讨论】:

references 电话帮助了我!【参考方案4】:

不确定这是否有用,但这在 Rails 4 中对我有用

Foo.where.not(bar: nil)

【讨论】:

这是最好的答案。 -- 2017 robots.thoughtbot.com/activerecords-wherenot【参考方案5】:

使用 Rails 4 很容易:

 Foo.includes(:bar).where.not(bars: id: nil)

另请参阅: http://guides.rubyonrails.org/active_record_querying.html#not-conditions

【讨论】:

以上是关于Rails where 条件使用 NOT NIL的主要内容,如果未能解决你的问题,请参考以下文章

Ruby on Rails - Criteria - Mongoid - where 条件为 2 x 2 列

Rails 审核搜索

ActiveRecord Arel OR 条件

Rails 中的多个查询条件 - 如果它们存在。

Rails:在 where 语句中使用大于/小于

Rails:使用 where 子句查找深度嵌套的关联