简体   繁体   English

如何在Rails中组合内部联接和左侧外部联接

[英]How to combine inner join and left outer join in Rails

I have two Models invoice and payments . 我有两个模型invoicepayments The relationship between them is invoice has_many payments . 它们之间的关系是has_many payments invoice

I'm using the following left outer join to return all invoices that have not been paid at all: 我正在使用以下左外部联接来返回所有尚未支付的所有发票:

result1 = Invoice.includes(:payments).where(:payments => { :id => nil })

I'm also interested in all invoices that have been partially paid. 我也对所有已部分支付的发票感兴趣。 To return those I use an inner join: 为了返回那些,我使用一个内部联接:

result2 = Invoice.joins(:payments).group("transfers.id").having("sum(payments.amount) < invoice.amount")

I would now like to combine the two results, ie I want all Invoices that have either not been paid, or not been fully paid. 现在,我想将这两个结果结合起来,即我想要所有尚未付款或未全部付款的发票。 I know that I could just do result = result1 + result2 . 我知道我可以做result = result1 + result2 However, this doesn't return an ActiveRecord object. 但是,这不会返回ActiveRecord对象。 Is there a way to combine those two queries in a single query? 有没有一种方法可以将这两个查询合并到一个查询中?

I use Rails 4.1 and PostgreSQL 我使用Rails 4.1和PostgreSQL

I believe you're correct that you can't get ActiveRecord to generate anything other than an inner join without writing it yourself. 我相信您是对的,除非您自己编写,否则无法让ActiveRecord生成除内部联接以外的任何东西。 But I wouldn't use includes in this context, for while it does happen to cause ActiveRecord to generate a different join, that is an "implementation detail" -- it's fundamental purpose is to load the associated models, which is not necessarily what you want. 但是我不会在这种情况下使用includes ,因为它确实会导致ActiveRecord生成不同的联接,这是“实现细节”-它的基本目的是加载关联的模型,而这不一定是您所需要的。想。

In your proposed solution, I don't understand why you'd group by both invoices.id and payments.id -- that seems to defeat the purpose of grouping. 在您提出的解决方案中,我不明白为什么要同时对invoices.idpayments.id进行分组-这似乎违背了分组的目的。

You could do something like the following, although I can't say that this seems much more Rails-ish. 您可以执行以下操作,尽管我不能说这看起来更像是Rails式的。

Invoice.joins("LEFT JOIN payments ON payments.transfer_id = invoices.id")
  .select("invoices.id, SUM(payments.amount) AS total_paid")
  .group("invoices.id")
  .having("SUM(payments.amount) IS NULL OR SUM(payments.amount) < invoices.amount")

This will return a list of Invoice objects with only the id field and total_paid field set. 这将返回仅设置id字段和total_paid字段的发票对象列表。 If you need other fields available, add them to both the select statement and the the group statement. 如果需要其他可用字段,请将它们添加到select语句和group语句中。

I ended up doing just a LEFT OUTER JOIN. 我最终只是做一个左外连接。 However, Rails doesn't seem to support group and having with the LEFT OUTER JOIN generated by includes() . 但是,Rails似乎不支持group并且having includes()生成的LEFT OUTER JOIN。 Therefore, i had to construct the join manually which actually doesn't feel like "the Rails way". 因此,我不得不手动构造实际上不像“ Rails方式”的联接。

My query: 我的查询:

    Invoice.joins("LEFT JOIN payments ON payments.transfer_id = invoices.id").group("invoices.id, payments.id").having("((payments.id IS NULL) OR (sum(payments.amount) < invoices.amount))")

Update : This doesn't work as expected. 更新 :不能按预期工作。 Please see the accepted answer. 请查看已接受的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM