Oracleのセットアップ:
CREATE TABLE invoices (i_id, invoice_number, creation_date, i_amount) AS
SELECT 1, 100000000, DATE '2016-01-01', 30 FROM DUAL UNION ALL
SELECT 2, 100000001, DATE '2016-02-01', 25 FROM DUAL UNION ALL
SELECT 3, 100000002, DATE '2016-03-01', 13 FROM DUAL UNION ALL
SELECT 4, 100000003, DATE '2016-04-01', 18 FROM DUAL UNION ALL
SELECT 5, 100000004, DATE '2016-05-01', 12 FROM DUAL;
CREATE TABLE payments (p_id, reference, received_date, p_amount) AS
SELECT 1, 'PAYMENT01', DATE '2016-01-12', 12 FROM DUAL UNION ALL
SELECT 2, 'PAYMENT02', DATE '2016-01-13', 28 FROM DUAL UNION ALL
SELECT 3, 'PAYMENT03', DATE '2016-02-08', 2 FROM DUAL UNION ALL
SELECT 4, 'PAYMENT04', DATE '2016-02-23', 30 FROM DUAL UNION ALL
SELECT 5, 'PAYMENT05', DATE '2016-05-12', 15 FROM DUAL;
クエリ:
WITH total_invoices (i_id, invoice_number, creation_date, i_amount, i_total) AS (
SELECT i.*,
SUM(i_amount) OVER (ORDER BY creation_date, i_id)
FROM invoices i
),
total_payments (p_id, reference, received_date, p_amount, p_total) AS (
SELECT p.*,
SUM(p_amount) OVER (ORDER BY received_date, p_id)
FROM payments p
)
SELECT invoice_number,
reference,
LEAST(p_total, i_total)
- GREATEST(p_total - p_amount, i_total - i_amount) AS used_pay_amount,
GREATEST(i_total - p_total, 0) AS open_inv_amount
FROM total_invoices
INNER JOIN
total_payments
ON ( i_total - i_amount < p_total
AND i_total > p_total - p_amount);
説明:
2つのサブクエリファクタリング(WITH ... AS()
)句は、invoices
テーブルとpayments
テーブルに追加の仮想カラムを追加し、請求書/支払い額の累計を加算します。
請求書(支払)が置かれる前の累積金額とその後の累積金額(支払済)として、各請求書(または支払)に範囲を関連付けることができます。これらの範囲が重複する場合は、2つのテーブルを結合することができます。
open_inv_amount
は請求された累積金額と支払った累積金額の正の差です。
used_pay_amount
は若干複雑ですが、現在の累積請求書と支払合計の差額と以前の累積請求書と支払合計額の差が大きいことを確認する必要があります。
出力:
INVOICE_NUMBER REFERENCE USED_PAY_AMOUNT OPEN_INV_AMOUNT
-------------- --------- --------------- ---------------
100000000 PAYMENT01 12 18
100000000 PAYMENT02 18 0
100000001 PAYMENT02 10 15
100000001 PAYMENT03 2 13
100000001 PAYMENT04 13 0
100000002 PAYMENT04 13 0
100000003 PAYMENT04 4 14
100000003 PAYMENT05 14 0
100000004 PAYMENT05 1 11
更新:
データを結合するためにUNION
を使用するmathguyの方法に基づき、私は再使用して私のコードの一部を別の解決策を考え出しました。
WITH combined (invoice_number, reference, i_amt, i_total, p_amt, p_total, total) AS (
SELECT invoice_number,
NULL,
i_amount,
SUM(i_amount) OVER (ORDER BY creation_date, i_id),
NULL,
NULL,
SUM(i_amount) OVER (ORDER BY creation_date, i_id)
FROM invoices
UNION ALL
SELECT NULL,
reference,
NULL,
NULL,
p_amount,
SUM(p_amount) OVER (ORDER BY received_date, p_id),
SUM(p_amount) OVER (ORDER BY received_date, p_id)
FROM payments
ORDER BY 7,
2 NULLS LAST,
1 NULLS LAST
),
filled (invoice_number, reference, i_prev, i_total, p_prev, p_total) AS (
SELECT FIRST_VALUE(invoice_number) IGNORE NULLS OVER (ORDER BY ROWNUM ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING),
FIRST_VALUE(reference) IGNORE NULLS OVER (ORDER BY ROWNUM ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING),
FIRST_VALUE(i_total - i_amt) IGNORE NULLS OVER (ORDER BY ROWNUM ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING),
FIRST_VALUE(i_total) IGNORE NULLS OVER (ORDER BY ROWNUM ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING),
FIRST_VALUE(p_total - p_amt) IGNORE NULLS OVER (ORDER BY ROWNUM ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING),
COALESCE(
p_total,
LEAD(p_total) IGNORE NULLS OVER (ORDER BY ROWNUM),
LAG(p_total) IGNORE NULLS OVER (ORDER BY ROWNUM)
)
FROM combined
),
vals (invoice_number, reference, upa, oia, prev_invoice) AS (
SELECT invoice_number,
reference,
COALESCE(LEAST(p_total - i_total) - GREATEST(p_prev, i_prev), 0),
GREATEST(i_total - p_total, 0),
LAG(invoice_number) OVER (ORDER BY ROWNUM)
FROM filled
)
SELECT invoice_number,
reference,
upa AS used_pay_amount,
oia AS open_inv_amount
FROM vals
WHERE upa > 0
OR (reference IS NULL AND invoice_number <> prev_invoice AND oia > 0);
説明:
combined
サブクエリファクタリング句はUNION ALL
を持つ2つのテーブルを結合し、請求して支払われた金額の累積合計を生成します。最後の行は、行の昇順累計で行を並べ替えることです(結び付いている場合は、作成された順に請求書の前に支払いを行います)。
filled
サブクエリファクタリング句は、以前に生成されたテーブルを埋めるので、値がNULLの場合は、次の非ヌル行から値を取得します(支払いがない請求書がある場合、前の行からの前払いの合計を見つける)。
サブクエリファクタリング句は、前のクエリと同じ計算を適用します(上記参照)。また、完全に未払いの請求書の識別に役立つprev_invoice
列が追加されています。
最後のSELECT
は値を取り、不要な行を除外します。
これはPL/Sqlで実践することができますが、正直なところ私はそれが単純なSQLでできているのではないかと疑う余地があります – Gar
あなたの例は間違っています - 支払い4の場合、あなたは13 + 13 + 14を持っています。これは30ではなく40になります。 – Boneist
@Boneist - 私は先に進み、その誤りを訂正しました。私も 'oracle 12'タグを削除しました。この問題は、Oracleのずっと下のバージョンでは解決できます。 – mathguy