Гость
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Работа с нарастающим итогом до определенного числа / 18 сообщений из 18, страница 1 из 1
18.01.2022, 11:20
    #40127386
Pata12kv
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Всем добрый день.

Помогите решить проблему. Есть таблица с данными:
INSTRUCTION_NUMTOTAL_WEIGHT175.492249.176343.164412.210512.016612.6787116.592843.074980.6410100.1761150.5921263.24139.2821437.512159.3961646.1117204.61828.080199.2822027.36


Цель решения, сделать нарастающий итог по столбцу TOTAL_WEIGHT, сортируя по полю INSTRUCTION_NUM, и если сумма нарастающего итога <= 200, начинать суммировать заново со следующей строки, пример какой результат я хочу получить, в столбце желаемый результат и выделил жирным шрифтом, где должен быть обрыв расчета:
INSTRUCTION_NUMTOTAL_WEIGHT Желаемый результат 175.49275.492249.176124.668343.164167.832412.210180.042512.016 192.058 612.67812.6787116.592129.27843.074 172.344 980.6480.6410100.176 180.816 1150.59250.5921263.24113.832139.282123.1141437.512160.626159.396 170.022 1646.11 46.11 17204.6 204.6 1828.08028.080199.28237.3622027.36 64.722

Буду очень благодарен за помощь.
...
Рейтинг: 0 / 0
18.01.2022, 13:37
    #40127424
DanilaSP
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Попробовал написать через рекурсивное cte (исходная таблица была сокращена):

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
declare @Table1 table (INSTRUCTION_NUM integer, TOTAL_WEIGHT decimal(10, 3));
insert into @Table1 values (1, 12.678), (2, 116.592), (3, 43.074), (4, 80.64), (5, 100.176), (6, 50.592), (7, 204.6), (8, 28.080);

with cte (INSTRUCTION_NUM, TOTAL_WEIGHT, RESULT) as (
	select INSTRUCTION_NUM, TOTAL_WEIGHT, TOTAL_WEIGHT from @Table1
	where INSTRUCTION_NUM = 1
	UNION ALL
	select t.INSTRUCTION_NUM, t.TOTAL_WEIGHT, cast(c.RESULT + t.TOTAL_WEIGHT as decimal(10, 3))
	from cte c 
	join @Table1 t on c.INSTRUCTION_NUM + 1 = t.INSTRUCTION_NUM
	where c.RESULT + t.TOTAL_WEIGHT <= 200.0
	UNION ALL
	select t.INSTRUCTION_NUM, t.TOTAL_WEIGHT, t.TOTAL_WEIGHT
	from cte c 
	join @Table1 t on c.INSTRUCTION_NUM + 1 = t.INSTRUCTION_NUM
	where c.RESULT + t.TOTAL_WEIGHT > 200.0
	)
select * from cte
...
Рейтинг: 0 / 0
18.01.2022, 16:17
    #40127501
Akina
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Попробовал нарисовать... https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=abcadb7c36ee71c795155b2a8ebe9b2e (WHERE curgrp < 10 добавлен для принудительного обрывания рекурсии).
Но непонятка - оконная SUM() в рекурсивной части даёт не тот результат, что ожидается... это вообще разрешается - оконные в рекурсии?
...
Рейтинг: 0 / 0
18.01.2022, 17:26
    #40127515
DanilaSP
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Забыл добавить, что последней строчкой надо написать:
Код: sql
1.
OPTION (MAXRECURSION 0)

, тогда не будет ограничения на длину таблицы.

На 100'000 строк работает 1 сек, на 1'000'000 - 12 сек.
...
Рейтинг: 0 / 0
18.01.2022, 18:12
    #40127542
Pata12kv
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
DanilaSP,

Спасибо, все получилось.
...
Рейтинг: 0 / 0
18.01.2022, 18:12
    #40127544
Pata12kv
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Akina,

Спасибо, все получилось тоже )
...
Рейтинг: 0 / 0
18.01.2022, 19:20
    #40127558
spenov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
сочинил запрос без рекурсии :)

Код: 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.
drop table if exists #test
create table #test (i int, j money)

insert #test
  values (1, 10)
        ,(2, 100)
        ,(3, 50)
        ,(4, 50)
        ,(5, 100)
        ,(6, 100)
        ,(7, 60)
        ,(8, 60)
        ,(9, 60)
        ,(10, 60)
;
--с рекурсией
with
  d as (
    select 
	     i
	    ,j
	    ,total=j
	  from #test
	  where i=1
    union all
	select
	     t.i
	    ,t.j
	    ,total=iif(d.total+t.j>200, t.j, d.total+t.j)
	  from #test t
	  inner join d on d.i+1=t.i
  )
  select 
       i
      ,total
    from d 
	order by i
;
--без рекурсии
with 
  i as (--получение всех интервалов суммой меньше 200
	select 
         l=l.i
        ,r=r.i
	  from #test l
	  inner join #test r on r.i>=l.i
	  where exists(select * --сумма между границами меньше 200
                     from #test
                     where i between l.i and r.i
                     having sum(j)<200)
         or l.i=r.i
  )
 ,i2 as (--исключить интервалы, которые входят в более крупный интервал
    select *
      from i
	  where not exists(select * --если интервал внутри другого интервала, то его исключаем
                         from i d
                         where d.l<=i.l
                           and d.r>=i.r
                           and (d.l<>i.l or d.r<>i.r)) 
  )
 ,i3 as (--оставить только непересекающиеся интервалы
    select *
      from i2 i
	  where not exists(select * --пересекающийся интервал слева
                         from i d
                         where d.l<i.l
                           and d.r>=i.l)
	     or not exists(select * --пересекающийся интервал справа
                         from i d
                         where d.l<=i.r
                           and d.r>i.r)
  )
  select
        t.i
        --,i.l
        --,i.r
        ,total=sum(s.j)
	from #test t
	inner join i3 i on i.l<=t.i and t.i<=i.r  --интервал, в который входит строка
	inner join #test s on s.i>=i.l and s.i<=t.i --строки от начала интервала до строки, по которым суммируем итог
	group by t.i
	        --,i.l
	        --,i.r
    order by t.i
...
Рейтинг: 0 / 0
19.01.2022, 01:26
    #40127638
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Pata12kv,

задача решается с помощью таблицы чисел без каких-либо громоздких вычислений, а способов формирования таблицы чисел - вагон и малая тележка, можно найти как здесь, так и в инете.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
drop table if exists #test, #range;
create table #test (i int, j money)

insert #test values
	(1, 10),(2, 100),(3, 50),(4, 50),(5, 100),(6, 100),(7, 60),(8, 60),(9, 60),(10, 60);

/* таблица чисел */
create table #range (r int);
insert #range (r) values
	(200),(400),(600),(800),(1000);
create nonclustered index ix1 on #range (r);

/* сам запрос */
with cte1 as
(
	select i nn, j, sum(j) over(order by i) itog
	from #test
)
select nn, j, itog, itog - min_r + 200 itog1, r1.min_r
from cte1
cross apply (select min(r) min_r from #range where itog < r ) r1(min_r)
order by nn
...
Рейтинг: 0 / 0
19.01.2022, 07:01
    #40127650
Павел Воронцов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Владислав Колосов,

Можно и без промежуточной таблицы.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
create table #test (i int, j money)

insert #test values
	(1, 10),(2, 100),(3, 50),(4, 50),(5, 100),(6, 100),(7, 60),(8, 60),(9, 60),(10, 60);

/* сам запрос */
with cte1 as
(
	select i nn, j, sum(j) over(order by i) itog, floor((sum(j) over(order by i)) / 200)  as n
	from #test
)
select nn, j, itog - n*200 as itog
from cte1
order by nn
drop table  #test


Только это неправильно, обрыв должен быть не там. Сек.
...
Рейтинг: 0 / 0
19.01.2022, 08:41
    #40127662
spenov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Владислав Колосов,

почему то у вашего решения результат не тот, который требуется в исходной формулировке.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
nn          j                     itog                  itog1                 min_r
----------- --------------------- --------------------- --------------------- -----------
1           10,00                 10,00                 10,00                 200
2           100,00                110,00                110,00                200
3           50,00                 160,00                160,00                200
4           50,00                 210,00                10,00                 400
5           100,00                310,00                110,00                400
6           1000,00               1310,00               NULL                  NULL
7           100,00                1410,00               NULL                  NULL
8           60,00                 1470,00               NULL                  NULL
9           60,00                 1530,00               NULL                  NULL
10          60,00                 1590,00               NULL                  NULL
11          60,00                 1650,00               NULL                  NULL
...
Рейтинг: 0 / 0
19.01.2022, 14:05
    #40127782
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
spenov,

добавьте еще числа в таблицу чисел. Сформируйте ее как вам надо, догадаться не сложно по-моему.
...
Рейтинг: 0 / 0
19.01.2022, 14:45
    #40127793
spenov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Владислав Колосов,

в строке 4 неправильное значение. должно быть 50, а у вас получается 10. если конечно я правильно угадал в какую колонку смотреть.
...
Рейтинг: 0 / 0
19.01.2022, 18:42
    #40127901
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
spenov,

возможно, я неправильно понял задачу, я предполагал, что вы хотите, чтобы нарастающий итог уменьшался кратно 200 при превышении границы. Тогда понадобится расчет нарастающего итога дважды, сначала для разбиения на секции, как в моем решении, а затем подсчет итога по каждой секции.
...
Рейтинг: 0 / 0
19.01.2022, 19:14
    #40127914
spenov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Владислав Колосов,

я не имею цели доковыряться. задача интересная.


я не понимаю как взятие по модулю может решить эту задачу. по мере накопления итогов появятся ошибки определения точек перехода.
...
Рейтинг: 0 / 0
17.02.2022, 18:29
    #40134938
Pata12kv
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
DanilaSP,

Хочу еще раз поблагодарить, все получилось как в вашем примере. Но столкнулся еще с одной сложностью.

Есть два критерия это суммарный вес и объем, я думал у меня получится как в решении что вы предложили, добавить еще и объем, это означает, я начинаю суммировать по отдельности два столбца, объем и вес, и при достижении определенного значения (в примере 200 кг или объем 1,6), начинать суммировать заново с новой строки, но скрипт зависает.

Как видно в примере, у меня есть объем и вес, я начинаю суммировать и на сумме третьей строки с четвертой, объем превышает допустимый 1,5, хотя вес еще позволяет добавлять, но мне уже нужно отсекать и начинать суммировать заново, так как я дошел до максимального объема, а вот на сумме строк 7 и 8мой, у меня уже вес превышает допустимый и я так же должен отсекать и начинать суммировать заново.

INSTRUCTION_NUM TOTAL_WEIGHT TOTAL_VOL Подсчет Объем 1.5 Подсчет Вес 200 Отсчка1 75.492 0.5 0.5 75.492 2 49.176 0.7 1.2 124.668 3 43.164 0.2 1.4 167.832 14 12.21 0.4 0.4 12.21 5 12.016 0.2 0.6 24.226 6 12.678 0.1 0.7 36.904 7 125.5 0.1 0.8 162.404 18 43.074 0.5 0.5 43.074 9 80.64 0.5 1 123.714 10 50.3 0.3 1.3 174.014 1
...
Рейтинг: 0 / 0
17.02.2022, 21:48
    #40134973
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Код: 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.
declare @t table (id int, v decimal(10, 4));

insert into @t
values
(1, 75.492), (2, 49.176), (3, 43.164), (4, 12.210),
(5, 12.016), (6, 12.678), (7, 116.592), (8, 43.074),
(9, 80.64), (10, 100.176), (11, 50.592), (12, 63.24),
(13, 9.282), (14, 37.512), (15, 9.396), (16, 46.11),
(17, 204.6), (18, 28.080), (19, 9.282), (20, 27.36);

with t as
(
 select
  *, sum(v) over (order by id) as sv
 from
  @t
)
select
 id, v, sv, cast(sv / 200 as int), 
 sv - first_value(sv - v) over (partition by cast(sv / 200 as int) order by id)
from
 t
order by
 id;
...
Рейтинг: 0 / 0
17.02.2022, 22:16
    #40134977
ValK412
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
Pata12kv,
Код: 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.
with testData as (select * from (values 
(1,	75.492,	0.5,	0.5,	75.492,0)	
,(2,	49.176,	0.7,	1.2,	124.668,0)
,(3,	43.164,	0.2,	1.4,	167.832,	1)
,(4,	12.21,	0.4,	0.4,	12.21,0)	
,(5,	12.016,	0.2,	0.6,	24.226,0)	
,(6,	12.678,	0.1,	0.7,	36.904,0)
,(7,	125.5,	0.1,	0.8,	162.404,	1)
,(8,    43.074,	0.5,	0.5,	43.074,0)	
,(9,	80.64,	0.5,	1.0,	123.714,0)
,(10,	50.3,	0.3,	1.3,	174.014,	1))
as t(nn,wt,vol,volGrT,wtGrT,grT)
)
,rt as (
select nn,1 as grN, cast(wt as float) as grWt,cast(vol as float) as grVol, wt, vol ,wtGrT,volGrT,grT
	from testData where nn=(select min(nn) from testData)
union all
select t.nn
	,case when ((rt.grWt+t.wt)>200.0) or((rt.grVol+t.Vol)>1.5) then grN+1 else grN end
	,case when ((rt.grWt+t.wt)>200.0) or((rt.grVol+t.Vol)>1.5) then t.wt else rt.grWt+t.wt end
	,case when ((rt.grWt+t.wt)>200.0) or((rt.grVol+t.Vol)>1.5) then t.vol else rt.grVol+t.vol end
	,t.wt,t.vol,t.wtGrT,t.volGrT,t.grT
   from rt inner join testData t on t.nn=rt.nn+1
)
--select * from rt
select grn,max(grWt) as grWt,max(grVol) as grVol
from rt
group by grn


результат с тестовыми данными
nn grN grWt grVol wt vol wtGrT volGrT grT1 1 75,492 0,5 75.492 0.5 75.492 0.5 02 1 124,668 1,2 49.176 0.7 124.668 1.2 03 1 167,832 1,4 43.164 0.2 167.832 1.4 14 2 12,21 0,4 12.210 0.4 12.210 0.4 05 2 24,226 0,6 12.016 0.2 24.226 0.6 06 2 36,904 0,7 12.678 0.1 36.904 0.7 07 2 162,404 0,8 125.500 0.1 162.404 0.8 18 3 43,074 0,5 43.074 0.5 43.074 0.5 09 3 123,714 1 80.640 0.5 123.714 1.0 010 3 174,014 1,3 50.300 0.3 174.014 1.3 1
...
Рейтинг: 0 / 0
17.02.2022, 22:49
    #40134983
ValK412
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Работа с нарастающим итогом до определенного числа
invm,
хотел такое решение... Спасибо за пример с FIRST_VALUE.
Код: sql
1.
2.
3.
select
 id, v, sv, cast(sv / 200 as int), 
 sv - first_value(sv - v) over (partition by cast(sv / 200 as int) order by id)
...
Рейтинг: 0 / 0
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Работа с нарастающим итогом до определенного числа / 18 сообщений из 18, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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