Гость
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / запрос с агрегацией по условию / 10 сообщений из 10, страница 1 из 1
21.04.2020, 21:22
    #39949674
swirls0506
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
Всем привет

Возникла задача сделать выборку с аггрегацией по заданному условию.
поясню на примере

модель Item-Attribute многие ко многим

Код: 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.
CREATE TABLE #Item (
 id INT PRIMARY KEY NOT NULL
 ,Name VARCHAR(50) NULL
 )

 CREATE TABLE #Attribute (
 id INT PRIMARY KEY NOT NULL
 ,Name VARCHAR(50) NULL
 )

 CREATE TABLE #ItemAttribute (
 itemid INT NOT NULL,
 attrid INT NOT NULL
 ,Desk VARCHAR(50) NULL
 )

INSERT #Item ( id ,Name )
VALUES ( 1 ,'Item1' )
 ,( 2 ,'Item12' )
 ,( 3 ,'Item13' )
 ,( 4 ,'Item14' )
 ,( 5 ,'Item15' )

 INSERT #Attribute ( id ,Name )
VALUES ( 1 ,'Attribute1' )
 ,( 2 ,'Attribute2' )
 ,( 3 ,'Attribute3' )
 ,( 4 ,'Attribute4' )
 ,( 5 ,'Attribute5' )

 INSERT #ItemAttribute ( itemid , attrid , Desk  )
VALUES ( 1 ,1 ,'Desc1' )
 ,( 2 ,1 ,'Desc2' )
 ,( 3 ,2 ,'Desc3' )
 ,( 4 ,3 ,'Desc4' )
 ,( 5 ,2 ,'Desc5' )
 ,( 1 ,2 ,'Desc1' )
 ,( 2 ,2 ,'Desc2' )
 ,( 3 ,3 ,'Desc3' )
 ,( 4 ,5 ,'Desc4' )
 ,( 1 ,4 ,'Desc1' )
 ,( 2 ,5 ,'Desc2' )
 ,( 3 ,4 ,'Desc3' )
 ,( 4 ,4 ,'Desc4' )
 ,( 5 ,1 ,'Desc3' )
 ,( 5 ,3 ,'Desc3' )
 ,( 5 ,5 ,'Desc4' )
  



нужно получить список itemid таких что для них существуют атрибуты связанные неким логическим выражением для attrid.
например ((attrid = 1) OR (attrid = 2)) AND ((attrid = 3) OR (attrid = 4))

получилось такое решение

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
запрос

select *
from 
(
SELECT t1.Itemid, (  SELECT '#' + ltrim(str(attrid))  FROM #ItemAttribute WHERE itemid=t1.Itemid FOR XML PATH('') ) + '#'  as attrs
FROM #ItemAttribute  t1
group by t1.Itemid
) as tt
WHERE (  (attrs LIKE '%#1#%')  OR (attrs LIKE '%#2#%')  )  AND (  (attrs LIKE '%#3#%')  OR (attrs LIKE '%#4#%')  )

результат:

1	#1#2#4#
3	#2#3#4#
5	#2#1#3#5#



Существует ли более красивое и оптимальное решение?

версия сервера - Microsoft SQL Server 2016
...
Рейтинг: 0 / 0
21.04.2020, 21:52
    #39949703
alexeyvg
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
swirls0506
нужно получить список itemid таких что для них существуют атрибуты связанные неким логическим выражением для attrid.
например ((attrid = 1) OR (attrid = 2)) AND ((attrid = 3) OR (attrid = 4))
Ну, можно сделать тупо экзистами.
Наверняка это будет быстрее для больших объёмов:
Код: 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.
select *
from #Item i
where 
  (
    exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 1)
    )
    or exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 2)
    )
  )
  and
  (
    exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 3)
    )
    or exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 4)
  )
)

...
Рейтинг: 0 / 0
22.04.2020, 11:22
    #39949910
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
swirls0506,

а где там агрегирующая функция? Вы написали distinct выражение через группировку.
...
Рейтинг: 0 / 0
22.04.2020, 12:02
    #39949936
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
swirls0506,

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
with t as
(
 select
  itemid,
  sum(distinct case when attrid = 1 then 1 when attrid = 2 then 2 when attrid = 3 then 4 when attrid = 4 then 8 else 0 end) s
 from
  #ItemAttribute
 group by
  itemid
)
select
 itemid
from
 t
where
 s & 3 > 0 and s & 12 > 0;
...
Рейтинг: 0 / 0
22.04.2020, 14:32
    #39950006
swirls0506
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
alexeyvg

Ну, можно сделать тупо экзистами.
Наверняка это будет быстрее для больших объёмов:


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

а где там агрегирующая функция? Вы написали distinct выражение через группировку.


а ее там и нет
я написал " с агрегацией по заданному условию"...

ну а что такое агрегация вопрос философский , можно понимать по разному
соглашусь с определение из вики "Агрегация, или агрегирование - процесс объединения элементов в одну систему."
...
Рейтинг: 0 / 0
22.04.2020, 14:52
    #39950016
swirls0506
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
invm,

спасибо
по скорости сравним с моим вариантом
а вот в качестве шаблона для генерации запроса его сложнее будет использовать - условие на атрибуты может меняться,
количество атрибутов задействованных также может быть разным
...
Рейтинг: 0 / 0
22.04.2020, 16:47
    #39950069
alexeyvg
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
swirls0506
по скорости сравним с моим вариантом
а вот в качестве шаблона для генерации запроса его сложнее будет использовать - условие на атрибуты может меняться,
количество атрибутов задействованных также может быть разным
По сложности написания условий он не хуже, там просто атрибуты транслируются в битовую маску, вместо строки у вас.

По скорости должно быть чуть быстрее, т.к. формируется не набор строк, а набор чисел (скажем, целых)
Но не принципиально быстрее, т.к. всё равно придётся просканировать таблицу.
А в варианте с exists есть маленький шансик, что обойдётся без сканирования.

Если подмножество item в запросе небольшое (ограничено условиями), ИМХО лучше вариант ваш или invm, ибо читаться будет проще...
swirls0506
по скорости сравним с моим вариантом
Кстати, ещё же нужно добавить условие attrid in (1,2,3,4)
Если выборка по небольшому подмножеству атрибутов, то будет сильно быстрее.
...
Рейтинг: 0 / 0
22.04.2020, 17:00
    #39950079
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
swirls0506,

вы ничего не агрегируете, только удаляете из результата дубликаты строк. Использовать для этого выражение группировки не особо принято, так же, как и называть это "агрегацией".
...
Рейтинг: 0 / 0
22.04.2020, 17:04
    #39950082
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
запрос с агрегацией по условию
swirls0506
по скорости сравним с моим вариантом
Значит неправильно измеряли или на малом объеме.

При большом объеме данных, каждый exists будет сканировать таблицу или индекс. Поэтому, чем больше exists'ов тем тормознее будет запрос.

Код: 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.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
CREATE TABLE #Item (
 id INT PRIMARY KEY NOT NULL
 ,Name VARCHAR(50) NULL
 )

 CREATE TABLE #Attribute (
 id INT PRIMARY KEY NOT NULL
 ,Name VARCHAR(50) NULL
 )

 CREATE TABLE #ItemAttribute (
 itemid INT NOT NULL,
 attrid INT NOT NULL
 ,Desk VARCHAR(50) NULL
 )
go

INSERT #Item ( id ,Name )
select top (500000)
 row_number() over (order by 1/0),
 'Item-' + cast(newid() as varchar(36))
from
 master.dbo.spt_values a cross join
 master.dbo.spt_values b;

 INSERT #Attribute ( id ,Name )
VALUES ( 1 ,'Attribute1' )
 ,( 2 ,'Attribute2' )
 ,( 3 ,'Attribute3' )
 ,( 4 ,'Attribute4' )
 ,( 5 ,'Attribute5' )

 INSERT #ItemAttribute ( itemid , attrid , Desk  )
 select
  i.id, a.id, 
 'Desc-' + cast(newid() as varchar(36))
 from
  #Item i cross apply
  (select top (cast(rand(checksum(i.id, i.Name)) * 5 + 1 as int)) id from #Attribute order by newid()) a

create index IX_#ItemAttribute on #ItemAttribute (itemid, attrid);
go

declare @itemid int;

set statistics xml, time on;

select
 @itemid = itemid
from 
(
SELECT t1.Itemid, (  SELECT '#' + ltrim(str(attrid))  FROM #ItemAttribute WHERE itemid=t1.Itemid FOR XML PATH('') ) + '#'  as attrs
FROM #ItemAttribute  t1
group by t1.Itemid
) as tt
WHERE (  (attrs LIKE '%#1#%')  OR (attrs LIKE '%#2#%')  )  AND (  (attrs LIKE '%#3#%')  OR (attrs LIKE '%#4#%')  )
option
 (maxdop 1);

select
 @itemid = i.id
from #Item i
where 
  (
    exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 1)
    )
    or exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 2)
    )
  )
  and
  (
    exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 3)
    )
    or exists(
      SELECT *
      FROM #ItemAttribute  ia
      where ia.itemid = i.id
        and (ia.attrid = 4)
  )
)
option
 (maxdop 1);

with t as
(
 select
  itemid,
  sum(distinct case when attrid = 1 then 1 when attrid = 2 then 2 when attrid = 3 then 4 when attrid = 4 then 8 else 0 end) s
 from
  #ItemAttribute
 group by
  itemid
)
select
 @itemid = itemid
from
 t
where
 s & 3 > 0 and s & 12 > 0
option
 (maxdop 1);

set statistics xml, time off;
go

drop table #ItemAttribute, #Attribute, #Item;
go



Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
(1 row affected)

 SQL Server Execution Times:
   CPU time = 4391 ms,  elapsed time = 4476 ms.
SQL Server parse and compile time: 
   CPU time = 182 ms, elapsed time = 182 ms.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 782 ms,  elapsed time = 889 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 2 ms.

(1 row affected)

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


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