Этот баннер — требование Роскомнадзора для исполнения 152 ФЗ.
«На сайте осуществляется обработка файлов cookie, необходимых для работы сайта, а также для анализа использования сайта и улучшения предоставляемых сервисов с использованием метрической программы Яндекс.Метрика. Продолжая использовать сайт, вы даёте согласие с использованием данных технологий».
Политика конфиденциальности
|
|
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
Доброе утро! Просьба помочь решить задачу, уже несколько дней как застрял на ней, просто туплю... Есть исходная таблица с суммой реализации и оплатами. Нужно на каждый день, за заданный период построить баланс и дату возникновения задолженности если такая была. Период сгенерировал с 01-01-2019 по 31-03-2019, на каждый день по каждому договору, т.к. потом должно загружаться в BI. Входящие оплаты со знаком "-", реализация со знаком "+". Поля: Договор - идентификатор договора id - уникальный идентификатор оплаты или отгрузки Дата - дата, когда проводилась реализация или поступила оплата ДатаВознДЗ - это пример того, какой результат должен быть получен. Нужно найти дату возникновения задолженности (первую не погашенную реализацию). Имеем несколько входящих операций (до начала расчётного периода с 01-01-2019 по 31-03-2019) - id 1-2-3, которые создают входящую задолженность. После идут обычные операции оплаты и реализации. Во вложении Excel пример с комментариями как должно быть на выходе (результирующий столбец ДатаВознДЗ). Смотрел тему http://www.sql.ru/forum/622067/raschet-zadolzhennosti-po-metodu-fifo. Не помогло... Как пробовал решить: 1. Создавал накопительный итог по каждой строчке, после анализировал его. Если предоплата, то ДатаВознДЗ = 9999-12-31, иначе Дата реализации. Но такой вариант не учитывает что задолженность образовалась раньше и не показывает именно первую не оплаченную реализацию 2. К п.1 добавил условие что если в предыдущей по дате операции накопительный итог был с задолженностью и с учётом текущей операции задолженность остаётся, то берём ДатаВознДЗ с предыдущего. Но такой вариант опять не работает когда входящая оплата гасит самую старую реализацию. 3. Это уже размышления. Может как-то через последующий обход данных через WHILE ? Решение ищу на обычном SQL или T-SQL. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2019, 08:16 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
Ну... освой накопительную сумму. Ничо тут нового нету. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2019, 09:20 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
Ща придут знатоки оконных функций и поправят... Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2019, 10:22 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
aleks222, Спасибо, но здесь не только накопительный итог. Если бы был просто накопительный итог и выявление даты смены знака накопительного итога, то через INNER JOIN alias это решалось бы на раз-два. Даже с последующей обработкой результатов. Я несколько увеличил исходную базу парой новых позиций и поправил цифры: Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. Если взять ваш пример решения, то после возникновения задолженности, берётся дата возникновения и она не меняется до момента, пока она не будет полностью погашена, хотя по мере поступления оплаты дата может меняться, т.к. идёт постепенное закрытие долгов. Забыл уточнить - в первую очередь все входящие оплаты "гасят" самую старую задолженность. На операции id 9 опять возникает задолженность с датой возникновения 30-01-2019 (всё корректно), остаток по которому 550. id 10-11 задолженность увеличивается. Заходит оплата от клиента (входящие платежи) id 12 и этот платёж больше чем не закрытый остаток по id 9 и он его закрывает с остатком 50 который частично покрывает реализацию id 10. И соответственно, уже первая не оплаченная реализации именно id 10 с датой 06-02-2019. После, заходит оплата от клиента id 13, которая покрывает не оплаченный остаток по id 10 и опять таки только частично закрывает id 11. И дата задолженности уже становится = дате операции по id 11. Вложил пример в Excel с комментариями, возможно так проще будет понять проблему. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2019, 15:25 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
на другой БД вот такое решают: Расчёт задолженности по LIFO ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2019, 15:40 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
MazoHist, Я переписал ваш пример под T-SQL, добавил новые операции. "+" это расходы (продаж, снятие средств со счёта и т.д.), "-" пополнение счёт, входящие платежи. Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. Что у меня получилось. Если ставить дату отсечения CAST(N'20091030' As Date), то считает корректно. Но если поставить дату расчёта '20091019' включительно, то оплата 7200 должна была погасить первую расходку и дата просрочки должна стать 15-10-2009. Посмотрите пожалуйста мой текущий исходник. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 09.06.2019, 08:59 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
MazoHist, Извините, это был не ваш пример, а другого форумчанина SkilledJunior Я его передал как смог под MS SQL: Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. Здесь опять таки, первая оплата -1000 закрывает 2 расходки, но дата всё равно вытягивается первой возникшей задолженности, а нужно первой не закрытой входящими платежами расходки. Касательно вашего примера, я не смог переписать на T-SQL запрос: Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. Возникает ошибка Column 'tmp2.DDATE' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause с указателем на строку "max(over_sum), ROW_NUMBER() over (partition by deal order by "DDATE" desc) AS liability_sum". Поможете исправить? Заранее спасибо. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 09.06.2019, 10:18 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
vladymyr.kMazoHist, Я переписал ваш пример под T-SQL, добавил новые операции. "+" это расходы (продаж, снятие средств со счёта и т.д.), "-" пополнение счёт, входящие платежи. Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. Что у меня получилось. Если ставить дату отсечения CAST(N'20091030' As Date), то считает корректно. Но если поставить дату расчёта '20091019' включительно, то оплата 7200 должна была погасить первую расходку и дата просрочки должна стать 15-10-2009. Посмотрите пожалуйста мой текущий исходник. Я не большой специалист по диалекту MS SQL, если в этом варианте условие на дату перенести в запрос w - ведь там считается накопительный итог? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 09.06.2019, 10:48 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
Удивительно, насколько замороченно решают простейшие задачи. Вас теории алгоритмов совсем не учили? Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 09.06.2019, 11:17 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
то же самое, но через оконные функции Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 09.06.2019, 11:28 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
aleks222, Огромное спасибо!!! Именно то, что нужно. Завтра уже спокойно сяду, разберу логику работы. P.S.: В обед чуть продвинулся в решении, только приходилось бы через какие-то грабли анализировать итог. ИМХО, данная задача имеет право перекочевать в FAQ. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 09.06.2019, 20:02 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
vladymyr.k, 2 vladymyr.k Наполнения двух стаканов накопленными суммами зависимо от нумерации операций и это не выглядит хорошо А терминах императивных алгоритмов задача решается надежно с привлечением очередей. Если поток входных данных отсортирован по договору, затем по дате события то достаточно одной простой очереди для возникающих просрочек, без поддержки приоритетов. В PL/SQL в качестве хранилища такой простой очереди в оперативной памяти хорошо годятся ассоциативные массивы. В T-SQL, вероятно, надо использовать индексированные табличные переменные В этом алгоритме есть детали, касающиеся правил работы с суммами оплаты. А именно, если в потоке данных оплата поступает по времени до наступления просрочки, необходимо решить, что с этим делать - генерировать сообщение об ошибке в потоке данных, тихо игнорировать такие суммы, как будто их не было, или накапливать как деньги, поступившие в счет будущих оплат. Тот же вопрос о допустимости накопдения должен решаться, когда поступившая сумма превышает сумму текущих просрочек. Учет такого рода деталей дает несовпадающие по поведению алгоритмы. Приложенная вами модель в Excel файле предполагает накопление суммы оплат, Во вложении пример, работающий исходя из накопления излишних оплат для будущего использования. в предположении, что данные курсора отсортированы по договорам и датам платежных событий общий алгоритм будет такой: Пока не пуст курсор платежей Цикл текущий договор = курсор.идентификатор_договора ЕСЛИ текущий договор <> предыдущий_договор TO СФОРМИРОВАТЬ_ИТОГОВЫЕ_ЗНАЧЕНИЯ_ПРЕДЫДУЩЕГО_ДОГОВОРА ОЧИСТИТЬ_ОЧЕРЕДЬ_ПРОСРОЧЕК КОНЕЦ_ЕСЛИ ЕСЛИ знак(курсор.сумма) = 1 ТО ДОБАВИТЬ_ПРОСРОЧКУ_В_ОЧЕРЕДЬ ИНАЧЕ_если знак(курсор.сумма) = -1 Сформировать общую сумму оплат КОНЕЦ_ЕСЛИ ЕСЛИ Суцмма оплат есть ОПЛАЧИВАТЬ_ПЕРВУЮ_В_ОЧЕРЕДИ_ПРОСРОЧКУ( сумма_оплат) -- этот метод должен возвращать неиспользованный остаток, когда по отношению к нему принимается решение о его дальнейшем использовании КОНЕЦ_ЕСЛИ курсор.следующая_запись Конец_Цикла В приложенном файле моделька решения задачи на VBA. я скопировал ваши данные, чтобы получить строки для еще одного договора. В клетке J2 нужно указать дату, на которую должен происходить расчет Нажатие кнопки рачет расчитывает состояние по просрочка, и выводит, начиная с L2 данные по обработанным договорам. ПРедварительно область исходных данных сортируется необходимым образом. То есть, в принципе можно дописывать в конец и смотреть, что получается после нажатия кнопки рассчитать. Код работы с очередью может показаться сложнее, чем нужно. Но причины этому просты: а) в vba нет встроенной поддержки ассоциативных массивов с целочисленными ключами б) реализацию очереди можно было бы построить на стандартном collection, но это неспортивно. У меня нет сомнения в том, что аналогичный по характеру код на pl/sql с использованием ассоциативных массивов обеспечит лучшую по отношению к аналитическим функциям производительность. Мне императивнный код кажется более управляемым. По отношению к T-SQL у меня нет сформированной системы ожиданий, но может быть и там подобный подход не полностью бессмысленный. Это вопрос эксперимента. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 10.06.2019, 01:00 |
|
||
|
Расчёт даты возникновения задолженности. T-SQL
|
|||
|---|---|---|---|
|
#18+
Народ, будьте проще и действуйте в лоб. Как показывает практика, так оно надёжнее и работать проще. На каждую новую запись в таблице операций вычисляем/анализируем значения трёх полей: 1. ID операции породившей задолженность (хоть по лифо, хоть по фифо) или ID задолженности (ID отдельной таблицы, где они хранятся) 2. Дата задолженности 3. Остаток по данной(!) задолженности (а не общий остаток по счёту накопительным итогом) Итак, долг возник. Заполняем поля задолженности значениями, тут всё очевидно. Затем долг начинает списываться, клиент вносит деньги. Сумма долга уменьшается (по модулю, если вы её решите хранить как отрицательную). Если она становится равной нулю, то тут два варианта: 1. Если общая сумма по счёту >=0 то всё опять же прозрачно. На данной записи поля по задолженности будут уже нулловыми. В отдельной таблице задолженность закрывается. 2. Если же общая сумма < 0 то дата задолженности и её ID (или ID породившей её операции) помогут в поиске следующей/предъидущей по времени задолженности (я не знаю, фифо у вас или лифо). Когда таковая найдена, то её значения ставятся в соответствующие поля. Система простая, наглядная, удобная. Можно по ней строить отчёты с минимумом вычислений, делать индексы, вьюхи и так далее. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 10.06.2019, 13:10 |
|
||
|
|

start [/forum/topic.php?fid=46&fpage=99&tid=1687700]: |
0ms |
get settings: |
6ms |
get forum list: |
18ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
30ms |
get topic data: |
15ms |
get forum data: |
3ms |
get page messages: |
108ms |
get tp. blocked users: |
2ms |
| others: | 220ms |
| total: | 408ms |

| 0 / 0 |
