powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / ABC анализ в sql
25 сообщений из 27, страница 1 из 2
ABC анализ в sql
    #37627463
PlanB
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый день!

Подскажите, пожалуйста, возможно ли сделать ABC-анализ запросом.
Краткое пояснение про ABC-анализ: одно из применений закона Парето 80/20.
Задаес две границы для определения зоны B. В данном примере (см. таблицу 1) это 80 и 95% от общего колличества. Весь крупняк по общей сумме меньший, чем 80% итога сносится в группу А.

Таблица1
NAMESUMABC_RAT|PROCPROC_SUMНаименование 115 429 930А|16%16%Наименование 213 497 652А|14%29%Наименование 312 796 790А|13%42%Наименование 412 209 909А|12%55%Наименование 512 022 677А|12%67%Наименование 611 529 588А|12%79%Наименование 710 942 376В|11%90%Наименование 87 166 437С|7%97%Наименование 92 730 968С|3%100%ИТОГО98 326 327X|100%200%*поля PROC и PROC_SUM - вспомогательные, для наглядности.

Спасибо!
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37627784
Фотография Knyazev Alexey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
declare @t table ( name char(1), val int )
insert into @t
values (1,	15429930),(2,	13497652),(3,	12796790),
(4,	12209909), (5,	12022677), (6,	11529588),
(7,	10942376), (8,	7166437), (9,	2730968)

select name, val
     , cast( val*1./a * 100 as decimal(10,2) ) [percent]
     , cast( b*1./a * 100 as decimal(10,2) ) [percent_sum], 
case 
when cast( b*1./a * 100 as decimal(10,2) )  between 0 and 80 then 'A'
when cast( b*1./a * 100 as decimal(10,2) )  > 95 then 'C'
else 'B' end [ABC]
from @t t
cross apply ( select sum(val) a from @t) t1
cross apply ( select sum(val) b from @t where val >= t.val ) t2
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37627908
andrey odegov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
declare @t table ( name char(1), val int )
insert into @t
values (1, 15429930), (2, 13497652), (3, 12796790)
     , (4, 12209909), (5, 12022677), (6, 11529588)
     , (7, 10942376), (8, 7166437), (9, 2730968);
select a.name, a.val, 100. * a.val / sum(a.val) over() as pct
     , 100. * sum(b.val) / sum(a.val) over() as run_pct
     , case
       when 100. * sum(b.val) / sum(a.val) over() > 95 then 'C'
       when 100. * sum(b.val) / sum(a.val) over() > 80 then 'B'
       else 'A' end as abc
from @t as a, @t as b
where b.val >= a.val
group by a.name, a.val, a.val;
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37628311
PlanB
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey odegov, спасибо большое!
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37628342
Фотография Дедушка
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PlanB,
и на каких у вас это объёмах данных?
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37628384
PlanB
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ДедушкаPlanB,
и на каких у вас это объёмах данных?не много, порядка 1000 строк
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37785997
ybuivovk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey odegov,
Здравствуйте Андрей!
У меня проблема запросом для ABC анализа.....не получается вывести накопительный %....я не знаю как уж его сделать...
вот пример кода:

declare @a int
declare @t table ( name int, sum_pokup int, Dolia int)

SELECT DISTINCT @a = sum(REWARD_TRNX_DETAIL.VOLUME_VALUE)
FROM
LOYALTY.REWARD_TRNX
LEFT OUTER JOIN LOYALTY.REWARD_TRNX_DETAIL
ON REWARD_TRNX_DETAIL.TRNX_ID = REWARD_TRNX.TRNX_ID
LEFT OUTER JOIN NODE.NODE
ON REWARD_TRNX.SYSTEM_NODE_ID = NODE.SYSTEM_NODE_ID

INSERT INTO @t
SELECT DISTINCT name = NODE.SYSTEM_NODE_ID --AS name
, sum_pokup = sum(REWARD_TRNX_DETAIL.VOLUME_VALUE) --AS sum_pokup
, Dolia = 100.* sum(REWARD_TRNX_DETAIL.VOLUME_VALUE) / @a --AS Dolia
FROM
LOYALTY.REWARD_TRNX
LEFT OUTER JOIN LOYALTY.REWARD_TRNX_DETAIL
ON REWARD_TRNX_DETAIL.TRNX_ID = REWARD_TRNX.TRNX_ID
LEFT OUTER JOIN NODE.NODE
ON REWARD_TRNX.SYSTEM_NODE_ID = NODE.SYSTEM_NODE_ID
WHERE
REWARD_TRNX.SYSTEM_NODE_ID IS NOT NULL
GROUP BY
NODE.SYSTEM_NODE_ID

SELECT DISTINCT b.name
, b.sum_pokup
, b.Dolia
-- , d.sum_pokup / b.sum_pokup AS Nakop_dolia ----вот эта сумма не правильно выводитсяююю

FROM
LOYALTY.REWARD_TRNX
LEFT OUTER JOIN LOYALTY.REWARD_TRNX_DETAIL
ON REWARD_TRNX_DETAIL.TRNX_ID = REWARD_TRNX.TRNX_ID
LEFT OUTER JOIN NODE.NODE
ON REWARD_TRNX.SYSTEM_NODE_ID = NODE.SYSTEM_NODE_ID
, @t AS b, @t AS d
WHERE
d.sum_pokup >= b.sum_pokup
AND REWARD_TRNX.SYSTEM_NODE_ID IS NOT NULL

GROUP BY
b.name
, b.sum_pokup
, b.Dolia
--, d.sum_pokup / b.sum_pokup ---накопительный процент

ORDER BY
b.sum_pokup DESC
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37785999
ybuivovk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если кто-то поможет, с меня причитается...честное слово!!!Я на все согласен, чтобы сделать эту хрень....
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37786352
aleks2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ybuivovk
Код: sql
1.
2.
3.
4.
5.
6.
7.
SELECT DISTINCT @a = sum(REWARD_TRNX_DETAIL.VOLUME_VALUE)
FROM
  LOYALTY.REWARD_TRNX
  LEFT OUTER JOIN LOYALTY.REWARD_TRNX_DETAIL
    ON REWARD_TRNX_DETAIL.TRNX_ID = REWARD_TRNX.TRNX_ID
  LEFT OUTER JOIN NODE.NODE
    ON REWARD_TRNX.SYSTEM_NODE_ID = NODE.SYSTEM_NODE_ID




Поведай нам сакральный смысл DISTINCT в энтом запросе.
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37788220
ybuivovk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aleks2,
Да это я так...скопировал с конструктора....
Я уже сделал ABC анализ по аналогии

declare @t table ( name char(1), val int )
insert into @t
values (1, 15429930), (2, 13497652), (3, 12796790)
, (4, 12209909), (5, 12022677), (6, 11529588)
, (7, 10942376), (8, 7166437), (9, 2730968);
select a.name, a.val, 100. * a.val / sum(a.val) over() as pct
, 100. * sum(b.val) / sum(a.val) over() as run_pct
, case
when 100. * sum(b.val) / sum(a.val) over() > 95 then 'C'
when 100. * sum(b.val) / sum(a.val) over() > 80 then 'B'
else 'A' end as abc
from @t as a, @t as b
where b.val >= a.val
group by a.name, a.val, a.val;

но дело в том, что.... допустим есть 2 одинаковых числа, то накопительный % тобишь " 100. * sum(b.val) / sum(a.val) over() as run_pct " не правильно считает. Он не суммирует процент....:( Как исправить не подскажете?
...
Рейтинг: 0 / 0
ABC анализ в sql
    #37788393
sqlinbox
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ybuivovk,

Код: 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.
declare @t table ( name char(1), val int )
insert into @t
values
(1, 15429930),
(2, 13497652),
(3, 12796790),
(4, 12209909),
(5, 12209909),
--(5, 12022677),  сделал 2 одинаковых значения
(6, 11529588),
(7, 10942376),
(8,  7166437),
(9,  2730968);

-- ваш вариант

select
a.name,
a.val,
100. * a.val / sum(a.val) over() as pct,
100. * sum(b.val) / sum(a.val) over() as run_pct,
case
when 100. * sum(b.val ) / sum(a.val) over() > 95 then 'C'
when 100. * sum(b.val ) / sum(a.val) over() > 80 then 'B'
else 'A' end as abc
from @t as a
full outer join @t b on 2>1
where b.val >= a.val
group by a.name, a.val
ORDER BY a.name DESC

-- исправленный вариант

declare @t2 table ( name char(1), val int, sm  Int)
insert into @t2
select name, val , 0
from @t order by val desc, name
declare @sm int = 0
update @t2 set @sm = @sm + val, sm = @sm from @t2

select
a.name,
a.val,
100. * a.val / @sm as pct,
100. * a.sm / @sm as run_pct,
case
when 100. * a.sm / @sm > 95 then 'C'
when 100. * a.sm / @sm > 80 then 'B'
else 'A' end as abc
from @t2 as a
order by 100. * a.sm / @sm desc
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
ABC анализ в sql
    #39184825
Mike_Gl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый !
Хочу апнуть темку, поднятую автором поста.
Для одного сплошного уровня, как в его примере, всё работает идеально.

Но в жизни необходимо считать АВС по группам, видам и т.п.
Бьюсь пару дней уже сам, но как-то не хватает объемного зрения и опыта, чтобы также в одном запросе решить эту задачку.
Исходный вариант от автора (уменьшил порядок чисел для наглядности):
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
declare @t table ( id int, val int )
	insert into @t
	values (1, 154), (2, 134), (3, 127)
		 , (4, 122), (5, 120), (6, 115)
		 , (7, 82), (8, 71), (9, 27);

	select 
		a.id, 
		a.val, 
		100. * a.val / sum(a.val) over() as pct
		 , 100. * sum(b.val) / sum(a.val) over() as run_pct
		 , case
		   when 100. * sum(b.val) / sum(a.val) over() > 95 then 'C'
		   when 100. * sum(b.val) / sum(a.val) over() > 80 then 'B'
		   else 'A' end as abc
	from @t as a, @t as b
	where b.val >= a.val
	group by a.id, a.val
	order by a.val desc



Давайте добавим в эту таблицу группы : PID = Parent ID, код родительской папки
Разбил все числа на две группы, по очереди, 1 / 2
и наша цель - посчитать АВС для каждой группы отдельно

Код: sql
1.
2.
3.
4.
5.
declare @t table ( id int, PID int, val int )
insert into @t
values (1, 1, 154), (2, 2, 134), (3, 1, 127)
     , (4, 2, 122), (5, 1, 120), (6, 2, 115)
     , (7, 1, 82), (8, 2, 71), (9, 1, 27);



При попытке сделать аналогично первому решению - в одном запросе - получается какая-то ерунда.
Даже не хочу выкладывать свои попытки.
Вот и прошу уважаемых форумчан помочь и посоветовать, как лучше рассчитать АВС-категории с учетом разбивки на группы.

Есть мысль пойти через врем.таблицу, куда в цикле размещать одну группу , т.е. приводить к 1му варианту или через CTE ...
В идеале нужно применять этот механизм для 3х- 4х вложенных групп (например, Фабрика-Группа-Коллекция).
Т.е. нужно посчитать, какую категорию будет иметь товар в каждом уровне вложения.
...
Рейтинг: 0 / 0
ABC анализ в sql
    #39184906
msLex
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Mike_Gl,

да вроде ничего сложного, просто добавить группировку по соответствующему полю

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
declare @t table ( id int, PID int, val int )
insert into @t
values (1, 1, 154), (2, 2, 134), (3, 1, 127)
     , (4, 2, 122), (5, 1, 120), (6, 2, 115)
     , (7, 1, 82), (8, 2, 71), (9, 1, 27);


select a.id, a.PID, a.val, 100. * a.val / sum(a.val) over(partition by a.PID) as pct
     , 100. * sum(b.val) / sum(a.val) over(partition by a.PID) as run_pct
     , case
       when 100. * sum(b.val) / sum(a.val) over(partition by a.PID) > 95 then 'C'
       when 100. * sum(b.val) / sum(a.val) over(partition by a.PID) > 80 then 'B'
       else 'A' end as abc
from @t as a
inner join @t as b on a.PID = b.PID
where b.val >= a.val
group by a.id, a.val, a.val, a.PID;
...
Рейтинг: 0 / 0
ABC анализ в sql
    #39185085
Mike_Gl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
msLexMike_Gl,

да вроде ничего сложного, просто добавить группировку по соответствующему полю


и правда работает
а я добавлял partition везде.. и в sum(b.val) тоже делал over(). Получается каша.
Спасибо большое!

А сам я что-то видимо не догоняю, не вижу, как оно должно работать :-(
...
Рейтинг: 0 / 0
ABC анализ в sql
    #39185086
Mike_Gl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
msLexMike_Gl,
Код: sql
1.
2.
3.
 
....
group by a.id, a.val, a.val, a.PID;



А зачем здесь два раза a.val ?
...
Рейтинг: 0 / 0
ABC анализ в sql
    #39185168
Фотография a_voronin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Mike_Gl,

Зачем тут вообще JOIN

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
declare @t table ( id int, PID int, val int )
insert into @t
values (1, 1, 154), (2, 2, 134), (3, 1, 127)
     , (4, 2, 122), (5, 1, 120), (6, 2, 115)
     , (7, 1, 82), (8, 2, 71), (9, 1, 27);

SELECT A.* ,100. * pct / run_pct,
	case
	when 100. * pct / run_pct < 55 then 'C'
	when 100. * pct / run_pct < 85 then 'B'
	else 'A' end as abc
FROM 
(
	select id, PID, val, sum(a.val) over(partition by a.PID ORDER BY val ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as pct
		 , sum(a.val) over(partition by a.PID) as run_pct
	from @t as a
) A
ORDER BY PID, val DESC 
...
Рейтинг: 0 / 0
ABC анализ в sql
    #39185179
Mike_Gl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
a_voroninMike_Gl,

Зачем тут вообще JOIN

Код: sql
1.
2.
3.
	(select id, PID, val, sum(a.val) over(partition by a.PID ORDER BY val ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as pct
) A
ORDER BY PID, val DESC 


Вау!
Такая конструкция для меня в новинку. Пошел читать мсдн.

А ответы получаются разные.
Идея примерно понятна, но надо разобраться, почему....
...
Рейтинг: 0 / 0
ABC анализ в sql
    #39185183
Фотография a_voronin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Mike_Gl,

Я проценты другие поставил , потому что все в C падало
...
Рейтинг: 0 / 0
ABC анализ в sql
    #39185249
msLex
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Mike_GlmsLexMike_Gl,
Код: sql
1.
2.
3.
 
....
group by a.id, a.val, a.val, a.PID;



А зачем здесь два раза a.val ?
х/з, я взял исходный запрос и просто добавил группировку
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
ABC анализ в sql
    #40068389
gerzzog
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день,
Спасибо за направление в решении вопроса АВС. Подскажите с одной проблемой
У меня есть рабочий код на вычисление АВС по двум критериям (названия магазина - market_id; название отдела - dep_code).
Мне нужно добавить еще фильтр по дате - название столбца date_start. Но если я его добавляю, пишет Invalid column name 'date_start'. Как добавить в этот код еще фильтр по дате?


drop table if exists #data
select good_id, dep_code, market_id,
qty = SUM(tsell_qty),
amt = SUM(tsell_amt),
buy = SUM(tbuy_amt)

into #data
from finance_agent
where date_start between '20210101' and '20210401'

group by market_id, dep_code, good_id

SELECT s.*,
per = 100. * cumtotal / nullif(s.total,0),
abc = case
when 100. * cumtotal / nullif(s.total,0) = Null then 'C'
when 100. * cumtotal / nullif(s.total,0) < 95 then 'C'
when 100. * cumtotal / nullif(s.total,0) < 80 then 'B'
else 'A' end
FROM
(
select good_id, dep_code, market_id, qty, amt, buy,
cumtotal = sum(s.amt) over (partition by dep_code, market_id order by amt rows between unbounded preceding and current row),
total = sum(s.amt) over (partition by dep_code, market_id)
from #data s
) S
order by dep_code, market_id desc
...
Рейтинг: 0 / 0
ABC анализ в sql
    #40068397
Владислав Колосов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
gerzzog,

скорее всего, таблица #data на момент выполнения существует и у нее другой набор столбцов. Замените имя таблицы #data в этом запросе на более сложное или выполняйте drop table в отдельном пакете.
...
Рейтинг: 0 / 0
ABC анализ в sql
    #40068480
gerzzog
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Владислав Колосов,
Попробовал изменить название - не помогло.
А вот drop table в отдельном пакете это для меня новое. Можете, пожалуйста, помочь с написанием кода.
Я в этом деле новичок, с предыдущим кодом мне помогли, а внести такое изменение сам не могу(
...
Рейтинг: 0 / 0
ABC анализ в sql
    #40068506
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Страдалец, тепузики фсе в отпуску. Надо выражопываться отчетливее.

gerzzog
Добрый день,
Мне нужно добавить еще фильтр по дате - название столбца date_start. Но если я его добавляю, пишет Invalid column name 'date_start'. Как добавить в этот код еще фильтр по дате?

drop table if exists #data
select good_id, dep_code, market_id,
qty = SUM(tsell_qty),
amt = SUM(tsell_amt),
buy = SUM(tbuy_amt)

into #data
from finance_agent
-- ЗДЕСЬ ЧТОЛИ? ---------------------------------------
where date_start between '20210101' and '20210401'
-------------------------------------------------------------
group by market_id, dep_code, good_id



У тебя в таблице
finance_agent
есть поле
date_start?

Нету его у тебя там. Вот сервер и возмущается.
...
Рейтинг: 0 / 0
ABC анализ в sql
    #40068517
gerzzog
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
есть там такое поле. В том то и дело.
То есть сейчас этот запрос работает. И он выделяет данные по команде where date_start between '20210101' and '20210401'
Но, если я захочу выбрать определенный период и еще добавить фильтр по этому столбцу, по аналогии с market_id, dep_code,
то он мне выкидывает эту ошибку.
Я даже пробовал добавить фильтр АВС не по дате, а по другой колонке из этой базы данных (например импортные поставщики) и он
тоже ругается Invalid column name
...
Рейтинг: 0 / 0
ABC анализ в sql
    #40068524
gerzzog
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вот даже для интереса попробовал изменить фильтр отдела (dep_code) на дополнительный фильтр, который не работает date_start.
И оно заработало. Но если добавляю опять же туда же dep_code третьим ранжиром, то ругается Invalid column name.
В общем не хочет он работать с любым третьим фильтром.
Может, действительно, нужно выполнить drop table в отдельном пакете. Как это реализовать?
...
Рейтинг: 0 / 0
25 сообщений из 27, страница 1 из 2
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / ABC анализ в sql
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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