Рассмотрим следующее:
платеж либо применяется полностью к балансу, либо частично применяется к балансу, либо переплачивает баланс.
Теперь представьте, что мы можем найти для любого баланса совокупный баланс счетов на сегодняшний день. Вместо того, чтобы представить это, давайте сделаем это:
create view cumulative_balance as
select a.*,
(select sum( balance )
from openitems b
where b.id = a.id and b.type = a.type and a.daysOpen >= a.daysOpen)
as cumulative_balance
from openitems a;
Теперь мы можем найти первый совокупный баланс, меньший или равный сумме платежа, для любого идентификатора и типа и сохранить его, daysOpen и совокупный баланс в переменных сервера.
Затем мы обновляем все openItems с этим идентификатором и типом, где daysOpen <= полученное нами значение, устанавливая все эти балансы на ноль. </p>
Затем мы находим первый ненулевой баланс этого идентификатора и типа и устанавливаем его баланс как его баланс - (оплата - накопленный баланс, который мы сохранили). при переплате этот баланс будет правильно отрицательным.
При правильном запросе вы сможете выполнить поиск и первое обновление в одном выражении.
Есть две проблемы. Одна из них заключается в том, что вы не можете определить два или более копий с одинаковыми идентификатором и типом и daysOpen, которые должны быть оплачены в первую очередь. Добавление уникального идентификатора к вашему столу послужило бы для этих случаев тай-брейком.
Во-вторых, необходимо сохранить совокупный баланс, чтобы использовать его в запросе для второго обновления. если вы правильно спроектировали свою таблицу со столбцом invoice_amount, который не был обновлен платежами, и столбцом платежей, который был, это решило бы вашу проблему.
Еще лучшим рефакторингом было бы иметь две таблицы, одну для счетов-фактур и одну для платежей: тогда представление может просто выполнять всю работу, сравнивая совокупные сальдо с совокупными платежами, создавая список неоплаченных сальдо или переплат.
На самом деле, я разработал именно такую систему для крупной ипотечной компании с инициалами FM. Это было немного сложнее, чем у вас, в том смысле, что сальдо были рассчитаны на основе ряда формул сумм и процентов, и нескольким плательщикам (фактически, страховщикам, это было для ипотечных кредитов, которые пошли на дефолт), приходилось выставлять счета в установленный порядок в соответствии с другими правилами, за каждую просроченную ипотеку.
Все это было сделано в представлениях с помощью короткой (100 строк или около того) хранимой процедуры, которая, по сути, выполняла то, что я изложил выше: использовала представление, которое упорядочивало выставление счетов по этим правилам, применяемые платежи (в вид), рассчитывая, какие дополнительные платежи необходимо выставить в счет, на какую дату, к какому страховщику. Затем хранимая процедура только что сгенерировала счета на текущую дату (какую текущую дату можно установить, опять же с использованием представления, на любую дату для целей тестирования).
(Ирония заключается в том, что я взял на себя обещание написать на С ++; единственный написанный мною С ++ использовал API Oracle и Sybase C для передачи данных из системы Oracle в систему Sybase.)