1

私のアプリでは、Invoice has_many item_numbersそしてInvoice has_many payments. 各請求書には、ItemNumber 金額属性の合計から Payment 金額属性の合計を差し引いた残高があります。

請求書モデルで残高を計算するのは非常に簡単ですが、請求書を残高でソートするクエリを作成しようとしていますが、これは ActiveRecord/SQL で行うのがはるかに難しいことがわかっています。

次のクエリを使用して、item_numbers の合計で請求書を注文することに成功しました (Daniel Rikowski に感謝します)。

Invoice.where(user_id: 1, deleted: false, status: 'Sent')
       .joins(:item_numbers)
       .select('invoices.*, sum(item_numbers.amount)')
       .group('invoices.id')
       .order('sum(item_numbers.amount) asc')
       .limit(20)

これを次のバランスで注文するように拡張しようとしました。

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}")/

このクエリには 2 つの問題があります。第一に、ひどく醜く、第二に、機能しません。すべての請求書に支払いがあるわけではないため、支払いテーブルで完全な外部結合を使用しました。joins(:payments) だけを使用した場合、支払いのない請求書は結果から除外されました。COALESCE は、ゼロ金額を処理するためにそこに配置されました。

クエリは近いですが、3 つの item_numbers と 1 つの支払い (非常に典型的なシナリオ) があるとします。支払い金額は 3 回差し引かれ、実際の金額よりもはるかに少ない残高になります (通常はマイナスの残高)。

私が自分の深みからどれだけ外れているかは、おそらくかなり明らかです。私はこのクエリに多くの労力を費やしました (約 4 時間の読み取りと試行の失敗)。私のデータベースは PostgreSQL です。

4

2 に答える 2

3

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;

2 つのテーブルをベース テーブルに結合するhas_manyと、行が互いに乗算され、完全に恣意的な結果になります。これは、ベース テーブルに結合するに合計を集計することで解決できます。

item_numbersまた、クエリにの結合条件がありませんでした。それはクロス結合につながります-非常に間違っているだけでなく、非常に高価です。(または、AR は外部キー関係から結合条件を自動的に導出するのに十分スマートですか? もしそうなら、なぜ 2 番目のテーブルの結合条件ですか?)のような列があると仮定item_numbersて、それを修正しました。invoice_idpayments

于 2013-01-01T11:26:14.243 に答える
2

あなたの問題は、列の乗算が原因です。Invoice に 1 つの Payment と 3 つの Item_numbers があるとします。通常の結合の結果は次のようになります。

| | 請求書.id | item_number.amount | お支払い金額 |
| | 1 | 4 | 5 |
| | 1 | 7 | 5 |
| | 1 | 2 | 5 |

このため、sum(payment.amount) は 5 ではなく 15 を返します。正しい合計を取得するには、合計を直接フェッチする必要があります。

Invoice.select('invoices.id, (SELECT SUM(item_numbers.amount) from item_numbers WHERE item_numbers.invoice_id = invoices.id) - (SELECT COALESCE(SUM(payments.amount),0) from payments WHERE payments.invoice_id = invoices.id) AS balance').group('invoices.id')
于 2013-01-01T12:08:59.500 に答える