Настройка 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);
Объяснение:
В двух подзапросах факторинга (WITH ... AS()
) просто добавьте дополнительный виртуальный столбец в таблицы invoices
и payments
с суммой суммы счета/суммы платежа.
Вы можете связать диапазон с каждым счетом (или оплатой) в качестве совокупной суммы, подлежащей оплате (выплатой) до выставления счета-фактуры (платежа), и совокупной суммы, подлежащей выплате. Затем эти две таблицы можно объединить, где есть перекрытие этих диапазонов.
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
Update:
на основе метода mathguy в использовании UNION
присоединиться к данным, я придумал другое решение повторно использовать некоторые из моего кода.
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
и генерирует суммарные итоги для сумм выставлением и оплаченных. Последнее, что он делает, - упорядочить строки по возрастающей совокупной сумме (и если есть связи, они будут помещать платежи, созданные по порядку, перед счетами-фактурами).
Предложение факторинга подзапроса должно заполнить ранее сгенерированную таблицу, так что если значение равно null, то оно примет значение из следующей непустой строки (и если есть счет-фактура без платежей, то он будет найдите общее количество предыдущих платежей из предыдущих строк).
Предложение факторизации подзапроса vals
использует те же вычисления, что и мой предыдущий запрос (см. Выше). Он также добавляет столбец prev_invoice
, чтобы помочь идентифицировать счета-фактуры, которые полностью неоплачиваются.
Окончательный SELECT
принимает значения и отфильтровывает ненужные строки.
Это было бы действительно выполнимо в PL/Sql, но, честно говоря, я очень сомневаюсь, что это можно сделать в простом sql – Gar
Ваш пример неверен - для оплаты 4 вы сказали, что сумма была 30, но в ваших выходных результатах, у вас есть 13 + 13 + 14, что составляет до 40, а не 30 ... – Boneist
@Boneist - Я пошел дальше и исправил эту ошибку. Я также удалил тег 'oracle 12' - эта проблема может быть решена в гораздо более низких версиях Oracle. – mathguy