Необходимо отображать все покупки клиента (сумму, дату, название) в в обратном хронологическом порядке, пока его кредит больше нуля. В последней строке должен отображаться остаток кредита.
DECLARE @Table1 table (Id_Client int, Value money) -- clients
-- Id_Client – client identifier, Value – loan amount
INSERT INTO @Table1 (Id_Client, Value)
SELECT 1, 24
UNION SELECT 2, 13
UNION SELECT 3, 2
UNION SELECT 4, 5
DECLARE @Table2 table (Id_Client int, DocDate datetime, Amount
money, Caption varchar(6)) -- purchases
-- Id_Client – client identifier, Amount – purchase amount,
DocDate – date of purchase, Caption – name of purchase
INSERT INTO @Table2 (Id_Client, Amount, DocDate, Caption)
SELECT 1, 5, '20051024', 'qh'
UNION SELECT 1, 9, '20051019', 'wj'
UNION SELECT 1, 3, '20051022', 'ek'
UNION SELECT 1, 8, '20051004', 'rl'
UNION SELECT 1, 6, '20051018', 'tz'
UNION SELECT 1, 5, '20050929', 'yx'
UNION SELECT 2, 11, '20051023', 'uc'
UNION SELECT 2, 6, '20051021', 'iv'
UNION SELECT 2, 45, '20051018', 'ob'
UNION SELECT 3, 4, '20051030', 'pn'
UNION SELECT 3, 2, '20051028', 'am'
UNION SELECT 4, 4, '20051021', 'sq'
UNION SELECT 4, 6, '20051023', 'dw'
UNION SELECT 4, 8, '20051023', 'fe'
UNION SELECT 4, 9, '20051023', 'gr'
Результат задачи должен быть таким
1 2005-10-24 00:00:00 5.00 qh
1 2005-10-22 00:00:00 3.00 ek
1 2005-10-19 00:00:00 9.00 wj
1 2005-10-18 00:00:00 6.00 tz
1 2005-10-04 00:00:00 1.00 rl
2 2005-10-23 00:00:00 11.00 uc
2 2005-10-21 00:00:00 2.00 iv
3 2005-10-30 00:00:00 2.00 pn
4 2005-10-23 00:00:00 5.00 gr
Мне удалось написать запрос, который отображает все покупки в обратном хронологическом порядке, но я так и не понял, как получить окончательный результат.
WITH AllPurchases AS (
SELECT
t2.Id_Client,
t2.DocDate,
t2.Amount,
t2.Caption
FROM
Table2 t2
WHERE
t2.Id_Client IN (SELECT Id_Client FROM Table1 WHERE Value > 0)
)
SELECT
ap.Id_Client,
ap.DocDate,
ap.Amount,
ap.Caption
FROM
AllPurchases ap
INNER JOIN Table1 t1 ON ap.Id_Client = t1.Id_Client
WHERE
t1.Value > 0
ORDER BY
ap.Id_Client ASC, ap.DocDate DESC
Я использую SQL-сервер 2022
Пользователю был предоставлен кредит в размере 24 единиц. Объяснение результата. Мы отображаем данные в хронологическом порядке. В результате получилось 5 строк:
1) 24 - 5 = 19, remaining credit > 0, output 5
2) 24 - 3 = 16, remaining credit > 0, output 3
3) 16 - 9 = 7, remaining credit > 0, output 9
4) 7 - 6 = 1, remaining credit > 0, output 6
5) Most recent spending in reverse chronological order
1 - 5, < 0, do not output this, but output 1, that is, the remaining balance before the credit is less than 0
Да, я использую SQL-сервер 2022.
Крайне неясно, как ожидаемый результат должен быть получен из исходных данных — не могли бы вы уточнить это, чтобы люди могли понять? Например, в результатах есть строка для Клиента 1 от 4 октября 2005 г. с заголовком «rl», но запись в таблице 2 для этого Клиента, Даты и Заголовка имеет другое значение. В вашем ожидаемом результате нет заголовков столбцов, которые бы указывали на то, что вы пытаетесь вычислить, и, судя по ответу, который я уже опубликовал, это не то, что подразумевается в тексте вашего вопроса, поэтому нам нужна более подробная информация, чтобы иметь возможность ответить.
После ваших правок до сих пор неясно, почему вы подаете заявку на кредит в ОБРАТНОМ хронологическом порядке. Похоже, кредит распространяется в первую очередь на самые последние покупки, а не на самые старые - верно?


Вы можете сделать это, используя оконные функции lag() для получения предыдущей строки и sum() для получения итоговой суммы:
AllPurchases используется для объединения данных из обеих таблиц и расчета промежуточной суммы.
cte2 используется для получения предыдущей промежуточной суммы для каждого клиента, которая будет использоваться в cte3 для расчета оставшегося кредита.
cte3 отвечает за определение оставшегося кредита на основе определенных условий, включая сравнение текущего «total» и предыдущего «running_total», полученного в cte2.
WITH AllPurchases AS (
SELECT
t2.Id_Client,
t2.DocDate,
t2.Amount,
t2.Caption,
t1.Value as total,
sum(Amount) over (partition by t2.Id_Client order by DocDate desc ROWS UNBOUNDED PRECEDING) as running_total
FROM
Table2 t2
INNER JOIN Table1 t1 ON t1.Id_Client = t2.Id_Client
),
cte2 as (
select *, lag(running_total) over (partition by Id_Client order by DocDate desc) as lag_running_total
from AllPurchases
),
cte3 as (
select *, case when total>= running_total then Amount
when lag_running_total is null then total
else total - lag_running_total
end as remaining_credit
from cte2
)
select Id_Client, DocDate, remaining_credit, Caption
from cte3
where remaining_credit > 0
Полученные результаты :
Id_Client DocDate remaining_credit Caption
1 2005-10-24 00:00:00.000 5.0000 qh
1 2005-10-22 00:00:00.000 3.0000 ek
1 2005-10-19 00:00:00.000 9.0000 wj
1 2005-10-18 00:00:00.000 6.0000 tz
1 2005-10-04 00:00:00.000 1.0000 rl
2 2005-10-23 00:00:00.000 11.0000 uc
2 2005-10-21 00:00:00.000 2.0000 iv
3 2005-10-30 00:00:00.000 2.0000 pn
4 2005-10-23 00:00:00.000 5.0000 dw
Вот альтернативная версия:
SELECT Id_Client
, DocDate
, CASE WHEN aggSum <= Value THEN Amount ELSE Amount + Value - aggSum END AS AmountTop
, Caption
FROM (
SELECT *
, ROW_NUMBER() OVER(PARTITION BY ID_Client, CASE WHEN aggSum >= Value THEN 0 ELSE 1 END ORDER BY DocDate DESC, Caption DESC) AS sort
FROM (
SELECT SUM(Amount) OVER(PARTITION BY t2.ID_Client ORDER BY DocDate DESC, Caption DESC/* ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW*/) AS aggSum, t2.*, t.Value
FROM @Table2 t2
INNER JOIN @Table1 t
ON t.Id_Client = t2.Id_Client
) x
) x
WHERE aggSum <= Value OR sort = 1
ORDER BY Id_Client, DocDate DESC, Caption DESC
Он вычисляет промежуточную сумму и сравнивает ее с доступным кредитом.
Разделы ROW_NUMBER() разбиты на две разные группы: те, где кредит меньше совокупной потраченной суммы, и те, где она больше.
CASE WHEN aggSum >= Value THEN 0 ELSE 1 END поэтому, если клиент превышает свой кредит в агрегированной сумме, он создает группу 0, в противном случае - группу 1, тогда ORDER BY DocDate DESC создает последовательность значений 1, 2, 3. Это означает, что первая строка, в которой клиент превышает кредит, получает значение номера строки = 1, вторая строка получит = 2 и т. д.
И поскольку нас интересует первая строка, в которой кредит клиента перерасходован, мы включаем эту строку в наш расчет в часть WHERE aggSum <= Value OR sort = 1.
Он обрабатывает строки с одинаковой датой, используя заголовок в качестве разрешения конфликтов.
Не могли бы вы объяснить ваш алгоритм более подробно? Я просто не понимаю, что делает переменная сортировки и какие значения она принимает.
Я отредактировал ответ, включив немного более длинное объяснение. Обычно полезно вывести значения этих временных столбцов, чтобы вы могли понять, что происходит.
Какую СУБД вы используете? Похоже на SQL Server, но какой версии?