使用NOT NIL条件的Rails


359

使用rails 3样式,我将如何写相反的东西:

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

我想找到id不为零的位置。我试过了:

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

但这返回:

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

这绝对不是我所需要的,并且几乎就像是ARel中的错误。


2
!niltrue在Ruby中评估为,在SQL查询中ARel转换true1。因此,生成的查询实际上就是您要的内容-这不是ARel错误。
yuval

Answers:


510

使用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))

1
这里的最后一个不适用于我,为此我们需要额外的gem或插件吗?我得到:rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas

3
@Tim是的,我在上面链接了MetaWhere宝石。
亚当·拉塞克

3
我喜欢不需要其他宝石的解决方案:)哪怕是有点难看
oreoshake

1
@ore​​oshake MetaWhere / Squeel非常值得拥有,这只是一个很小的方面。但是,当然,一般情况是很好的。
亚当·拉瑟克

1
@BKSpurgeon链接where条件只是在构建AST,直到您按下诸如each或的终端方法,它才会访问数据库to_a。建立查询与性能无关;您从数据库中请求的是。
亚当·拉瑟克

251

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

您想要的是:

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

2
我很好奇将nil变成'1'的逻辑是什么
SooDesuNe 2010年

12
猜测,!nil返回true,这是一个布尔值。:id => true使您id = 1掌握SQLese。
zetetic

这是避免编写原始sql片段的好方法。语法没有Squeel简洁。
开尔文2012年

1
我还没有使用sqlite3做到这一点。sqlite3想看field_name != 'NULL'
mjnissim

@zetetic除非您使用的是postgres,否则您将得到id = 't':)
thekingoftruth 2012年

36

对于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代码段,但是我想哈希条件在解析器中已更改为字符串?),因此请确保将引用添加到末尾:

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

DEPRECATION警告:您似乎渴望加载在字符串SQL代码段中引用的表(其中一个:....)。例如:

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

当前,Active Record可以识别字符串中的表,并且知道将注释表加入查询中,而不是在单独的查询中加载注释。但是,在不编写成熟的SQL分析器的情况下执行此操作本质上是有缺陷的。由于我们不想编写SQL解析器,因此我们将删除此功能。从现在开始,当您从字符串引用表时,必须显式告知Active Record:

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

1
references呼叫帮我!
theblang


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.