【问题标题】:Rails how to query has_many instance in condition of the instance not existRails如何在实例不存在的情况下查询has_many实例
【发布时间】:2017-09-08 06:01:50
【问题描述】:

请帮忙!

我需要将下面的这些查询子句合并为一个。

Foo.where('bar_id IS NULL')
Foo.joins(:bar).where('bar.daily_budget > bar.daily_cost')

joins(:bar) in 子句时,bar_id IS NULL 记录将被过滤。

像下面这样组合没那么简单,这个bar_id IS NULL会被过滤掉。

Foo.joins(:bar).where('bar.daily_budget > bar.daily_cost').where('bar_id IS NULL')

我想要的结果更像

bar_id is null or (bar_id is not null AND bars.daily_budget > bars.daily_cost)

Foo 结果需要 bar_id 为空,bars.daily_budget > bar.daily_cost 如果 bar 同时存在。

【问题讨论】:

    标签: mysql sql ruby-on-rails ruby-on-rails-4


    【解决方案1】:

    您真正想要的是 SQL UNION。

    您可以使用#or查询方法。诀窍是,您必须在两端都使用joins(:bar),以便传递给#的关系必须在结构上兼容。

    Foo.joins(:bar).where(:bar_id => nil)
                   .or(Foo.join(:bar).where('bar.daily_budget > bar.daily_cost'))
    # SELECT `foos`.* FROM `foos` INNER JOIN `bar` on `foos.bar_id` = `bar.id` WHERE(`foos`.`bar_id` is NULL OR bar.daily_budget > bar.daily_cost)
    

    注意:QueryMethod or 是在 Rails 5 中添加的。

    更新:正如 DanneManne 回答的那样,上面的方法不起作用,因为 .joins(:bar).where(:bar_id => nil) 实际上不会提供任何关于 INNER JOIN 的信息。需要的是 LEFT JOIN,可以通过以下方式完成:

    Foo.left_outer_joins(:bar).where('foo.bar_id is null or bar.daily_budget > bar.daily_cost')
    SELECT `foos`.* from `foos` LEFT OUTER JOIN `bar` ON `foos`.`bar_id` = `bar`.`id` WHERE (foo.bar_id is null or bar.daily_budget > bar.daily_cost)
    

    【讨论】:

    • 但是Foo.joins(:bar).where(bar_id: nil) 不兼容。 where(:bar_id => nil) 结果被过滤。我在控制台中尝试了Foo.where(bar_id: nil).or(Foo.joins(:bar).where('bar.daily_budget > bar.daily_cost')),然后我得到了那些错误ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:joins]
    • 试试Foo.left_outer_joins(:bar).where('foo.bar_id is null or bar.daily_budget > bar.daily_cost')
    【解决方案2】:

    调用.joins(:bar) 的问题在于它会默认创建一个INNER JOIN 数据库查询。当这种情况发生时,结果中不会包含bar_id 为空的任何记录。并且指定 where(bar_id: null) 不会改变这一点。

    但是,您可以通过将LEFT OUTER JOIN 字符串而不是符号传递给joins 方法来解决此问题,并从那里开始工作。例如:

    Foo.joins('LEFT OUTER JOIN bar ON foo.bar_id = bar.id')
       .where('foo.bar_id IS NULL OR bar.daily_budget > bar.daily_cost')
    

    请注意,您的表名很可能是复数形式,因此请确保查询与您的环境匹配。

    【讨论】:

    • 感谢LEFT OUTER JOIN ,它的作品。但是@kiddorails 的答案更短Foo.left_outer_joins(:bar)
    【解决方案3】:

    joins 在 Rails 中是 INNER JOIN 你需要的是OUTER JOIN

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-13
      • 2022-06-27
      • 2014-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-20
      • 1970-01-01
      相关资源
      最近更新 更多