powered by simpleCommunicator - 2.0.50     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Заменить курсор
11 сообщений из 11, страница 1 из 1
Заменить курсор
    #40097750
Alex_Va
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть таблица #debt_period_client в которой задолженности клиентов по отгрузкам на определённую дату суммированы по ответственному сотруднику и периоду просрочки:
Код: sql
1.
2.
3.
4.
5.
6.
7.
CREATE TABLE #debt_period_client(
	[report_date] [date],
	[period_id] [int],
	[manager] [nvarchar](255) ,
	[id_1c] [varchar](10) ,
	[debt_amount] [decimal](16, 2) ,
	[debt_amount_real] [decimal](16, 2) )



Код: 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.
insert into #debt_period_client ([report_date],[period_id],[manager],[id_1c],[debt_amount],[debt_amount_real])
values
('2021-09-01', 4,N'Менеджер 1', '01', 25000, 25000)
,('2021-09-01', 3,N'Менеджер 1', '01', 30000, 30000)
,('2021-09-01', 2,N'Менеджер 1', '01', 10000, 10000)
,('2021-09-02', 4,N'Менеджер 1', '01', 18000, 18000)
,('2021-09-02', 3,N'Менеджер 1', '01', 26000, 26000)
,('2021-09-02', 2,N'Менеджер 1', '01', 8000, 8000)
,('2021-09-03', 4,N'Менеджер 1', '01', 18000, 18000)
,('2021-09-03', 3,N'Менеджер 1', '01', 26000, 26000)
,('2021-09-03', 2,N'Менеджер 1', '01', 8000, 8000)
,('2021-09-01', 2,N'Менеджер 1', '02', 5000, 5000)
,('2021-09-01', 1,N'Менеджер 1', '02', 6000, 6000)
,('2021-09-02', 2,N'Менеджер 1', '02', 5000, 5000)
,('2021-09-02', 1,N'Менеджер 1', '02', 6000, 6000)
,('2021-09-03', 1,N'Менеджер 1', '02', 3000, 3000)
,('2021-09-01', 3,N'Менеджер 2', '03', 16000, 16000)
,('2021-09-01', 2,N'Менеджер 2', '03', 10000, 10000)
,('2021-09-01', 1,N'Менеджер 2', '03', 11000, 11000)
,('2021-09-02', 3,N'Менеджер 2', '03', 15000, 15000)
,('2021-09-02', 2,N'Менеджер 2', '03', 10000, 10000)
,('2021-09-02', 1,N'Менеджер 2', '03', 11000, 11000)
,('2021-09-03', 4,N'Менеджер 2', '03', 10000, 10000)
,('2021-09-03', 3,N'Менеджер 2', '03', 8000, 8000)
,('2021-09-03', 2,N'Менеджер 2', '03', 5000, 5000)
,('2021-09-01', 2,N'Менеджер 2', '04', 15000, 15000)
,('2021-09-01', 1,N'Менеджер 2', '04', 2000, 2000)
,('2021-09-02', 2,N'Менеджер 2', '04', 10000, 10000)
,('2021-09-02', 1,N'Менеджер 2', '04', 2000, 2000)
,('2021-09-03', 1,N'Менеджер 2', '04', 2500, 2500)



Поле [debt_amount] = [debt_amount_real] на данном этапе

Поле [period_id] ссылается на такую таблицу:
period_idperiod_dscrdays_startdays_end1до 3 дней0323-7 дней3737-15 дней715415-30 дней1530530-60 дней30606больше 60 дней6010000


Кроме того, у этих же клиентов могут быть предоплаты по другим заказам - таблица #client_debt_prepaiment
Код: sql
1.
2.
3.
4.
5.
6.
7.
IF object_id('tempdb..#client_debt_prepaiment') IS NOT NULL
	DROP TABLE #client_debt_prepaiment;
CREATE TABLE #client_debt_prepaiment (
	[report_date] [date],
	[manager] [nvarchar](255) ,
	[id_1c] [varchar](10) ,
	[prepaid_amount] [decimal](16, 2) )



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
insert into #client_debt_prepaiment ([report_date],[manager],[id_1c],[prepaid_amount])
values
('2021-09-01', N'Менеджер 1', '01', 40000)
,('2021-09-02', N'Менеджер 1', '01', 40000)
,('2021-09-03', N'Менеджер 1', '01', 10000)
,('2021-09-01', N'Менеджер 2', '04', 3000)
,('2021-09-02', N'Менеджер 2', '04', 1000)
,('2021-09-03', N'Менеджер 2', '04', 500)



Нужно написать скрипт, чтобы скорректировать задолженность клиентов с учётом предоплаты. Если у клиента есть предоплата - разнести эту сумму по периодам задолженности, начиная с более старого. Результат - в поле [debt_amount_real]

Например,
клиент 01 , Дата - 2021-09-01
Задолженности:
report_dateperiod_idmanagerid_1cdebt_amountdebt_amount_real01.09.20214Менеджер 101250002500001.09.20213Менеджер 101300003000001.09.20212Менеджер 1011000010000

Предоплата:
report_datemanagerid_1cprepaid_amount2021-09-01Менеджер 10140000.00

Результат должен быть:
report_datemanagerid_1cperiod_iddebt_amountdebt_amount_real2021-09-01Менеджер 101210000.0010000.002021-09-01Менеджер 101330000.0015000.002021-09-01Менеджер 101425000.000.00

Я ничего не смог придумать лучше, чем сделать курсор в курсоре: первый курсор перебирает предоплаты и изменяет таблицу задолженностей вторым курсором:

Код: 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.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
DECLARE @CURSOR AS CURSOR
DECLARE @report_date AS DATE
DECLARE @id_1c AS VARCHAR(10)
DECLARE @manager AS NVARCHAR(255)
DECLARE @prepaid_amount AS DECIMAL(16, 2)
DECLARE @debt_amount AS DECIMAL(16, 2)
DECLARE @period_id AS INT

SET @CURSOR = CURSOR SCROLL
FOR

SELECT report_date
	,manager
	,id_1c
	,prepaid_amount
FROM #client_debt_prepaiment

OPEN @CURSOR

FETCH NEXT
FROM @CURSOR
INTO @report_date
	,@manager
	,@id_1c
	,@prepaid_amount

WHILE @@FETCH_STATUS = 0
BEGIN
	DECLARE @CURSOR_small AS CURSOR

	SET @CURSOR_small = CURSOR SCROLL
	FOR

	SELECT period_id
		,debt_amount
	FROM #debt_period_client
	WHERE report_date = @report_date
		AND manager = @manager
		AND id_1c = @id_1c
	ORDER BY period_id DESC

	OPEN @CURSOR_small

	FETCH NEXT
	FROM @CURSOR_small
	INTO @period_id
		,@debt_amount

	WHILE @@FETCH_STATUS = 0
	BEGIN
		IF @debt_amount >= @prepaid_amount
		BEGIN
			UPDATE #debt_period_client
			SET debt_amount_real = (@debt_amount - @prepaid_amount)
			WHERE report_date = @report_date
				AND manager = @manager
				AND id_1c = @id_1c
				AND period_id = @period_id

			SET @prepaid_amount = 0
		END
		ELSE
		BEGIN
			SET @prepaid_amount = @prepaid_amount - @debt_amount

			UPDATE #debt_period_client
			SET debt_amount_real = 0
			WHERE report_date = @report_date
				AND manager = @manager
				AND id_1c = @id_1c
				AND period_id = @period_id
		END

		FETCH NEXT
		FROM @CURSOR_small
		INTO @period_id
			,@debt_amount
	END

	CLOSE @CURSOR_small

	DEALLOCATE @CURSOR_small

	FETCH NEXT
	FROM @CURSOR
	INTO @report_date
		,@manager
		,@id_1c
		,@prepaid_amount
END

CLOSE @CURSOR
DEALLOCATE @CURSOR



Сорри за простыню
Буду признателен, за более оптимальный вариант с "табличным" расчётом
...
Рейтинг: 0 / 0
Заменить курсор
    #40097752
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Разучить накопительный итог.
...
Рейтинг: 0 / 0
Заменить курсор
    #40097806
Oleg_SQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Alex_Va,

Не классика конечно, но:

Код: 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.
WITH CTE
     AS (SELECT a.*, 
                ROW_NUMBER() OVER(PARTITION BY a.manager, a.id_1c, a.report_date ORDER BY a.period_id DESC) AS rn, 
                DENSE_RANK() OVER(PARTITION BY a.manager, a.id_1c ORDER BY a.report_date) AS gr, 
                b.prepaid_amount
           FROM #debt_period_client AS a
                INNER JOIN #client_debt_prepaiment AS b ON a.manager = b.manager
                                                           AND a.id_1c = b.id_1c
                                                           AND a.report_date = b.report_date
          --WHERE a.manager = 'Менеджер 1'
		  ),
     CTE2
     AS (SELECT *, 
                LAG(debt_amount_real) OVER(PARTITION BY manager, gr ORDER BY rn) AS xx
           FROM CTE)
SELECT report_date,
       manager,
	   id_1c,
	   period_id,
	   debt_amount,
	   debt_amount_real =
		 CASE 
		   WHEN xx IS NULL THEN IIF(prepaid_amount > debt_amount_real, 0, debt_amount_real- prepaid_amount  )
		   ELSE IIF(debt_amount_real > (prepaid_amount - xx), debt_amount_real - (prepaid_amount - xx) , debt_amount_real)
		 END 
FROM CTE2
ORDER BY manager, report_date, id_1c, period_id





Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
report_date manager                   id_1c      period_id   debt_amount                             debt_amount_real
----------- ------------------------- ---------- ----------- --------------------------------------- ---------------------------------------
2021-09-01  Менеджер 1                01         2           10000.00                                10000.00
2021-09-01  Менеджер 1                01         3           30000.00                                15000.00
2021-09-01  Менеджер 1                01         4           25000.00                                0.00
2021-09-02  Менеджер 1                01         2           8000.00                                 8000.00
2021-09-02  Менеджер 1                01         3           26000.00                                4000.00
2021-09-02  Менеджер 1                01         4           18000.00                                0.00
2021-09-03  Менеджер 1                01         2           8000.00                                 24000.00
2021-09-03  Менеджер 1                01         3           26000.00                                34000.00
2021-09-03  Менеджер 1                01         4           18000.00                                8000.00
2021-09-01  Менеджер 2                04         1           2000.00                                 14000.00
2021-09-01  Менеджер 2                04         2           15000.00                                12000.00
2021-09-02  Менеджер 2                04         1           2000.00                                 11000.00
2021-09-02  Менеджер 2                04         2           10000.00                                9000.00
2021-09-03  Менеджер 2                04         1           2500.00                                 2000.00
...
Рейтинг: 0 / 0
Заменить курсор
    #40097811
Oleg_SQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Oleg_SQL,

скорее всего не всегда будет считать корректно...
...
Рейтинг: 0 / 0
Заменить курсор
    #40097816
Alex_Va
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Oleg_SQL,

Спасибо

Не по всем контрагентам сходится с моим результатом.
Постараюсь разобраться.
...
Рейтинг: 0 / 0
Заменить курсор
    #40097831
Oleg_SQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Alex_Va,



Вот алгоритм, проверял на всех возможных вариантах - работает.
@k - меняем (это аналог предоплаты)


Код: 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.
--declare @k int = 3100
DECLARE @k INT = 31000;
--declare @k int = 310000

WITH cte
     AS (SELECT id = 1, d = 10000, k = @k UNION
         SELECT id = 2, d = 12000, k = @k UNION
         SELECT id = 3, d = 8000, k = @k UNION
         SELECT id = 4, d = 19000, k = @k),
     cte2
     AS (SELECT a.*, 
                LAG(k - dd) OVER(ORDER BY id) AS calc
           FROM cte AS a
                OUTER APPLY
         (
             SELECT SUM(d) AS dd
               FROM cte AS b
              WHERE a.id >= b.Id
         ) AS c)
     SELECT a.id, a.d, a.k,
            CASE
                WHEN ISNULL(calc, k) >= d THEN 0
                WHEN ISNULL(calc, k) < d
                     AND ISNULL(calc, k) > 0 THEN d - ISNULL(calc, k)
                WHEN ISNULL(calc, k) <= 0 THEN d
                 ELSE ISNULL(calc, d)
            END AS [То что нужно], 
            a.d - CASE
                      WHEN ISNULL(calc, k) >= d THEN 0
                      WHEN ISNULL(calc, k) < d
                           AND ISNULL(calc, k) > 0 THEN d - ISNULL(calc, k)
                      WHEN ISNULL(calc, k) <= 0 THEN d
                       ELSE ISNULL(calc, d)
                  END AS [Проверка]
       FROM cte2 AS a;
...
Рейтинг: 0 / 0
Заменить курсор
    #40097962
Alex_Va
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Oleg_SQL,

Спасибо, что откликнулись
Пробую применить Ваш код.
...
Рейтинг: 0 / 0
Заменить курсор
    #40098008
Ролг Хупин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alex_Va
Oleg_SQL,

Спасибо, что откликнулись
Пробую применить Ваш код.


Не стейсняйтесь критиковать, здесь форум, мало ли что напишут
...
Рейтинг: 0 / 0
Заменить курсор
    #40098047
Oleg_SQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ролг Хупин
Alex_Va
Oleg_SQL,

Спасибо, что откликнулись
Пробую применить Ваш код.


Не стейсняйтесь критиковать, здесь форум, мало ли что напишут




Не постесняюсь спросить ваш вариант )))
Можно на моем примере с 4 строками для простоты понимания.
...
Рейтинг: 0 / 0
Заменить курсор
    #40098237
Oleg_SQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Alex_Va,

"Немного" переделал запрос - более краткого кода уже не придумаю.
Критике всегда рад. Я критику очень люблю )))

Код: 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.
DROP TABLE IF EXISTS #T;

DECLARE @pay1 INT = 18999, -- для 1-го манагера
             @pay2 INT = 10000, -- для 2-го
	     @pay3 INT = 550   -- для 3-го

SELECT *
 INTO #T 
 FROM (SELECT mng = 1, id = 1, [долг] = 10000, [платеж] = @pay1
       UNION SELECT mng = 1, id = 2, [долг] = 12000, [платеж] = @pay1
	   UNION SELECT mng = 1, id = 3, [долг] = 8000, [платеж] = @pay1 
	   UNION SELECT mng = 1, id = 4, [долг] = 19000, [платеж] = @pay1
	   
	   UNION SELECT mng = 2, id = 1, [долг] = 1500, [платеж] = @pay2
	   UNION SELECT mng = 2, id = 2, [долг] = 500, [платеж] = @pay2
	   UNION SELECT mng = 2, id = 3, [долг] = 100, [платеж] = @pay2
	   UNION SELECT mng = 2, id = 4, [долг] = 2500, [платеж] = @pay2

   	   UNION SELECT mng = 3, id = 1, [долг] = 600, [платеж] = @pay3
	   ) a;

;WITH CTE AS
(
SELECT * 
       ,SUM([долг]) OVER(PARTITION BY mng ORDER BY Id) ss
       ,[платеж] - SUM([долг]) OVER(PARTITION BY mng ORDER BY Id) [остаток]
 FROM #T
 )
 SELECT mng, id, [долг], [платеж],
	    CASE WHEN ISNULL(LAG([остаток]) OVER(PARTITION BY mng ORDER BY Id), [платеж]) <= 0 
                      THEN [долг]
                    ELSE 
                       ABS((ISNULL(LAG([остаток]) OVER(PARTITION BY mng ORDER BY Id), [платеж]) - IIF([остаток] < 0, 0, [остаток])) - [долг])
           END [остаток]
   FROM CTE
   ORDER BY mng, id;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
mng         id          долг        платеж      остаток
----------- ----------- ----------- ----------- -----------
1           1           10000       18999       0
1           2           12000       18999       3001
1           3           8000        18999       8000
1           4           19000       18999       19000

2           1           1500        10000       0
2           2           500         10000       0
2           3           100         10000       0
2           4           2500        10000       0

3           1           600         550         50
...
Рейтинг: 0 / 0
Заменить курсор
    #40098717
Alex_Va
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Oleg_SQL,

Отличное решение )

добавлю в расчёт ещё дату и контрагента
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Заменить курсор
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]