Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 按两个“之和”排序查询;“你有很多”;子表?_Sql_Ruby On Rails_Postgresql_Activerecord_Join - Fatal编程技术网

Sql 按两个“之和”排序查询;“你有很多”;子表?

Sql 按两个“之和”排序查询;“你有很多”;子表?,sql,ruby-on-rails,postgresql,activerecord,join,Sql,Ruby On Rails,Postgresql,Activerecord,Join,在我的应用程序中,发票有许多项目编号,发票有许多付款。每张发票都有一个余额,即ItemNumber金额属性之和减去付款金额属性之和 在发票模型中,余额很容易计算,但我正在尝试编写一个查询,按余额对发票进行排序,而在ActiveRecord/SQL中,这要困难得多 我已通过以下查询成功订购了所有商品编号的发票(感谢Daniel Rikowski): 我已尝试通过以下平衡将此扩展到订单: Invoice.where(user_id: 1, deleted: false, status: 'Sent'

在我的应用程序中,
发票有许多项目编号
发票有许多付款
。每张发票都有一个余额,即ItemNumber金额属性之和减去付款金额属性之和

在发票模型中,余额很容易计算,但我正在尝试编写一个查询,按余额对发票进行排序,而在ActiveRecord/SQL中,这要困难得多

我已通过以下查询成功订购了所有商品编号的发票(感谢Daniel Rikowski):

我已尝试通过以下平衡将此扩展到订单:

Invoice.where(user_id: 1, deleted: false, status: 'Sent')
       .joins(:item_numbers)
       .joins("FULL OUTER JOIN payments ON payments.invoice_id = invoices.id")
       .select("invoices.*, sum(item_numbers.amount_with_gst) - COALESCE(sum(payments.amount), 0)")
       .group("invoices.id")
       .order("sum(item_numbers.amount_with_gst) - COALESCE(sum(payments.amount), 0) #{dir}")/
此查询有两个问题。首先,它是可怕的丑陋,其次,它不工作。我在payments表上使用了完整的外部联接,因为并非所有发票都有付款,如果我只使用联接(:付款),则任何没有付款的发票都将从结果中排除。合并放在那里是为了处理零金额

查询很接近,但假设有3个项目编号和1个付款(一个非常典型的场景),付款金额将减去3倍,导致余额远小于实际金额(通常为负余额)


很可能很清楚我有多深。我在这个问题上花了很多精力(大约4个小时的阅读和失败的尝试),但还不能完全解决它。我的数据库是PostgreSQL。

不确定AR语法,但正确的查询应该是:

SELECT i.*, COALESCE(n.total, 0) - COALESCE(p.total, 0) AS balance
FROM   invoices i
LEFT   JOIN (
    SELECT invoice_id, sum(amount) AS total
    FROM   payments
    GROUP  BY invoice_id 
    ) p ON p.invoice_id = i.id
LEFT   JOIN (
    SELECT invoice_id, sum(amount_with_gst) AS total
    FROM   item_numbers
    GROUP  BY invoice_id 
    ) n ON n.invoice_id = i.id
WHERE  i.user_id = 1
AND    i.deleted = false
AND    i.status = 'Sent'
ORDER  BY balance;
如果将两个
具有多个
表的表连接到基表,则行会彼此相乘,从而产生完全任意的结果。您可以通过在加入基表之前聚合总计来解决这个问题


另外,我在您的查询中没有看到
项目编号
的联接条件。这将导致交叉连接-除了极其错误之外,成本极其高昂。(或者AR是否足够聪明,可以自动从外键关系派生连接条件?如果是,为什么要在第二个表中使用连接条件?)假设
项目编号
有一个
发票id
列,就像
付款
,我对此进行了修改。

不确定AR语法,但正确的查询应该是:

SELECT i.*, COALESCE(n.total, 0) - COALESCE(p.total, 0) AS balance
FROM   invoices i
LEFT   JOIN (
    SELECT invoice_id, sum(amount) AS total
    FROM   payments
    GROUP  BY invoice_id 
    ) p ON p.invoice_id = i.id
LEFT   JOIN (
    SELECT invoice_id, sum(amount_with_gst) AS total
    FROM   item_numbers
    GROUP  BY invoice_id 
    ) n ON n.invoice_id = i.id
WHERE  i.user_id = 1
AND    i.deleted = false
AND    i.status = 'Sent'
ORDER  BY balance;
如果将两个
具有多个
表的表连接到基表,则行会彼此相乘,从而产生完全任意的结果。您可以通过在加入基表之前聚合总计来解决这个问题


另外,我在您的查询中没有看到
项目编号
的联接条件。这将导致交叉连接-除了极其错误之外,成本极其高昂。(或者AR是否足够聪明,可以自动从外键关系派生联接条件?如果是,为什么第二个表中的联接条件?)假设
项目编号
有一个
发票id
列,就像
付款
,我修正了这一点。

您的问题是由列相乘引起的。想象一下,发票上有一笔付款和三个商品编号。常规联接的结果如下所示:

| invoice.id | item_number.amount | payment.amount | | 1 | 4 | 5 | | 1 | 7 | 5 | | 1 | 2 | 5 |
您的问题是由列相乘引起的。想象一下,发票上有一笔付款和三个商品编号。常规联接的结果如下所示:

| invoice.id | item_number.amount | payment.amount | | 1 | 4 | 5 | | 1 | 7 | 5 | | 1 | 2 | 5 |
这是一个完美的工作方式,还有一个额外的优点,就是我可以愉快地理解它。我甚至修改了一些现有的搜索查询,以同样的方式进行排序。但是,.group('invoices.id')似乎不再是必需的(可能是因为没有联接)。谢谢。@brad:请注意,对于结果中的多行,相关子查询通常比我演示的(
LEFT
JOIN
慢得多。有些人甚至称之为反模式。用测试差异。谢谢你的提醒。我会做更多的调查。我的数据库很小,我的查询也很小,所以它不会对我产生太大的影响(现在…)。这非常有效,而且还有一个额外的优点,那就是我可以愉快地理解它。我甚至修改了一些现有的搜索查询,以同样的方式进行排序。但是,.group('invoices.id')似乎不再是必需的(可能是因为没有联接)。谢谢。@brad:请注意,对于结果中的多行,相关子查询通常比我演示的(
LEFT
JOIN
慢得多。有些人甚至称之为反模式。用测试差异。谢谢你的提醒。我会做更多的调查。我的数据库很小,我的查询也很小,所以它不会对我有太大影响(现在…),非常感谢。如果我把你的答案写进发票里,你的答案确实有效。由于AR在这种情况下进行内部联接,因此项目号上没有联接条件(发票。联接(:项目号)=>选择“发票”。*从“发票”内部联接“项目号”上的“发票号”。“发票id”=“发票”。“id”)。我选择了另一个答案,因为它包含ActiveRecord解决方案,这是我在代码中使用的。不过,你的回答很有教育意义。非常感谢。如果我把你的答案写进发票里,你的答案确实有效。由于AR在这种情况下进行内部联接,因此项目号上没有联接条件(发票。联接(:项目号)=>选择“发票”。*从“发票”内部联接“项目号”上的“发票号”。“发票id”=“发票”。“id”)。我选择了另一个答案,因为它包含ActiveRecord解决方案,这是我在代码中使用的。不过,你的回答很有教育意义。