Гость
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Оконный DISTINCT COUNT / 11 сообщений из 11, страница 1 из 1
11.03.2015, 14:13
    #38901343
a_voronin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
Мне потребовалось посчитать оконный DISTINCT COUNT, который не поддерживается даже в SQL 2014.

Прошу высказаться по поводу оптимальности данного метода. Может у кого есть идеи получше.

И поправьте меня, если я не прав, но это должно работать начиная с SQL 2005.

Код: 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.
CREATE TABLE #T(ID INT, A INT, B INT)
GO
-- тестовые данные 
INSERT INTO #T(ID, A, B)
VALUES
(1,1,1),(2,1,2),(1,1,3),(3,2,1),(3,2,2),(1,2,3),(6,3,1),(6,3,2),
(1,3,3),(4,1,1),(4,1,2),(1,1,3),(3,2,1),(3,2,2),(1,2,3),(3,3,1),
(6,3,2),(1,3,3),(6,1,1),(5,1,2),(1,1,3),(7,2,1),(8,2,2),(9,2,3),
(0,3,1),(8,3,2),(1,3,3)
GO


-- Distinсt_COUNT с группировкой по двум полям 
SELECT Distinсt_COUNT = COUNT(DISTINCT ID), CNT = COUNT(*), A, B FROM #T
GROUP BY A, B
ORDER BY A, B
GO 
-- эмуляция оконного Distinсt_COUNT с группировкой по двум полям 

SELECT -- COUNT(DISTINCT ID) OVER (PARTITION BY A, B), --> Use of DISTINCT is not allowed with the OVER clause.
	Distinсt_COUNT = MAX(DR) OVER(PARTITION BY A, B), 
	CNT = COUNT(*) OVER(PARTITION BY A, B), 
	A, B 
FROM  
(
	SELECT DR = DENSE_RANK() OVER(PARTITION BY A, B ORDER BY ID), A, B FROM #T
) F
ORDER BY A, B
GO 

-- Distinсt_COUNT с группировкой по одному полю 
SELECT Distinсt_COUNT = COUNT(DISTINCT ID), CNT = COUNT(*), A FROM #T
GROUP BY A
ORDER BY A
GO 
-- эмуляция оконного Distinсt_COUNT с группировкой по одному полю 
SELECT -- COUNT(DISTINCT ID) OVER (PARTITION BY A), --> Use of DISTINCT is not allowed with the OVER clause.
	Distinсt_COUNT = MAX(DR) OVER (PARTITION BY A), A, B,
	CNT = COUNT(*) OVER (PARTITION BY A)
FROM  
(
	SELECT DR = DENSE_RANK() OVER(PARTITION BY A ORDER BY ID), A, B FROM #T
) F
ORDER BY A, B

GO 

DROP TABLE #T
GO
...
Рейтинг: 0 / 0
12.03.2015, 09:47
    #38902067
denvio
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
-- эмуляция оконного Distinсt_COUNT с группировкой по одному полю 
WITH C AS
(
SELECT A, B, 
	CASE
		WHEN ROW_NUMBER() OVER (PARTITION BY A, ID ORDER BY ID) = 1
		THEN ID
	END AS DC
FROM #T
)
SELECT Distinсt_COUNT = COUNT(DC) OVER (PARTITION BY A/* ORDER BY ID*/), A, B, CNT = COUNT(*) OVER (PARTITION BY A)
FROM C
ORDER BY A, B;



"Высокопроизводительный код T-SQL. Оконные функции" Ицик Бен-Ган
...
Рейтинг: 0 / 0
12.03.2015, 13:54
    #38902438
a_voronin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
denvio,

Планы почти одинаковые, но с Row_Number есть один лишний шаг.

Код: 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.
CREATE TABLE #T(ID INT, A INT, B INT)
GO
-- тестовые данные 
INSERT INTO #T(ID, A, B)
VALUES
(1,1,1),(2,1,2),(1,1,3),(3,2,1),(3,2,2),(1,2,3),(6,3,1),(6,3,2),
(1,3,3),(4,1,1),(4,1,2),(1,1,3),(3,2,1),(3,2,2),(1,2,3),(3,3,1),
(6,3,2),(1,3,3),(6,1,1),(5,1,2),(1,1,3),(7,2,1),(8,2,2),(9,2,3),
(0,3,1),(8,3,2),(1,3,3)
GO
DECLARE @I INT = 14
WHILE @I > 0 
BEGIN 
	SET @I -= 1; 
	INSERT INTO #T(ID, A, B)
	SELECT ID, A, B FROM #T
END 
GO

SET STATISTICS IO  ON

-- Distinсt_COUNT с группировкой по двум полям 
SELECT Distinсt_COUNT = COUNT(DISTINCT ID), CNT = COUNT(*), A, B FROM #T
GROUP BY A, B
ORDER BY A, B
GO 
-- эмуляция оконного Distinсt_COUNT с группировкой по двум полям 

SELECT -- COUNT(DISTINCT ID) OVER (PARTITION BY A, B), --> Use of DISTINCT is not allowed with the OVER clause.
	Distinсt_COUNT = MAX(DR) OVER(PARTITION BY A, B), 
	CNT = COUNT(*) OVER(PARTITION BY A, B), 
	A, B 
FROM  
(
	SELECT DR = DENSE_RANK() OVER(PARTITION BY A, B ORDER BY ID), A, B FROM #T
) F
ORDER BY A, B
GO 

-- Distinсt_COUNT с группировкой по одному полю 
SELECT Distinсt_COUNT = COUNT(DISTINCT ID), CNT = COUNT(*), A FROM #T
GROUP BY A
ORDER BY A
GO 
-- эмуляция оконного Distinсt_COUNT с группировкой по одному полю 
SELECT -- COUNT(DISTINCT ID) OVER (PARTITION BY A), --> Use of DISTINCT is not allowed with the OVER clause.
	Distinсt_COUNT = MAX(DR) OVER (PARTITION BY A), A, B,
	CNT = COUNT(*) OVER (PARTITION BY A)
FROM  
(
	SELECT DR = DENSE_RANK() OVER(PARTITION BY A ORDER BY ID), A, B FROM #T
) F
ORDER BY A, B
GO 

WITH C AS
(
SELECT A, B, 
	CASE
		WHEN ROW_NUMBER() OVER (PARTITION BY A, ID ORDER BY ID) = 1
		THEN ID
	END AS DC
FROM #T
)
SELECT Distinсt_COUNT = COUNT(DC) OVER (PARTITION BY A/* ORDER BY ID*/), A, B, CNT = COUNT(*) OVER (PARTITION BY A)
FROM C
ORDER BY A, B;
GO

DROP TABLE #T
GO

...
Рейтинг: 0 / 0
13.03.2015, 17:51
    #38904339
denvio
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
без параллелизма у меня вот такая картина.
Какому методу отдать предпочтение, вопрос пока открыт для меня...
...
Рейтинг: 0 / 0
13.03.2015, 18:08
    #38904371
0-0
0-0
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
denvio, a_voronin

посмотрите что будет по чтениям
...
Рейтинг: 0 / 0
13.03.2015, 20:25
    #38904499
a_voronin
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
0-0,

На моём 2014-ом CASE WHEN ROW_NUMBER() проигрывает DENSE_RANK на несколько процентов по всем параметрам
...
Рейтинг: 0 / 0
15.03.2015, 12:45
    #38905168
denvio
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
5 разных серверов, 3 снимка в разное время.
Чтение все время больше у ROW_NUMBER(), только вот Duration в большинстве случаев не в пользу DENSE_RANK
...
Рейтинг: 0 / 0
15.03.2015, 12:46
    #38905170
denvio
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
...
Рейтинг: 0 / 0
15.03.2015, 12:46
    #38905171
denvio
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
...
Рейтинг: 0 / 0
15.03.2015, 17:23
    #38905292
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
Как нужно тестировать и почему оконные функции не панацея
Код: 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.
use tempdb;
set nocount on;
go

create table dbo.t (id int identity primary key, g int, v int);
go

insert into dbo.t
 (g, v)
 select top (1000000)
  rand(checksum(newid())) * 1000,
  rand(checksum(newid())) * 100
 from
  master.dbo.spt_values a cross apply
  master.dbo.spt_values b;
go

--create index IX_t__g__v on dbo.t (g, v);
go

declare @r table (d varchar(30), m varchar(50));
declare @m varchar(50), @s1 varchar(max), @s2 varchar(max), @s3 varchar(max);

select
 @m = newid(),
 @s1 = '/*' + @m + '*/declare @c int;
select
 @c = max(x.dr) over (partition by x.g)
from  
(
 select g, dr = dense_rank() over(partition by g order by v) from dbo.t
) x
option (maxdop 1);'

insert into @r
values
 ('dense_rank() + max()', @m);

select
 @m = newid(),
 @s2 = '/*' + @m + '*/declare @c int;
select
 @c = count(x.f) over (partition by x.g)
from  
(
 select g, case when row_number() over(partition by g, v order by (select 1)) = 1 then 1 end as f from dbo.t
) x
option (maxdop 1);'

insert into @r
values
 ('row_number() + count()', @m);

select
 @m = newid(),
 @s3 = '/*' + @m + '*/declare @c int;
select
 @c = t.id + x.g
from
 dbo.t t join
 (
  select g, count(distinct v) as c from dbo.t group by g
 ) x on x.g = t.g
option (maxdop 1);'

insert into @r
values
 ('group by + count(distinct)', @m);

declare @c int = 0;
while @c < 5
 begin
  exec(@s1);
  exec(@s2);
  exec(@s3);

  select
   @c += 1;
 end;

select
 r.d as [description],
 e.[elapsed time, ms],
 e.[logical reads],
 qp.query_plan
from
 sys.dm_exec_query_stats s cross apply
 sys.dm_exec_sql_text(s.sql_handle) t cross apply
 sys.dm_exec_query_plan(s.plan_handle) qp join
 @r r on t.text like '%/*' + r.m + '*/%' cross apply
 (select s.total_elapsed_time / cast(s.execution_count as money) / 1000, s.total_logical_reads / execution_count) as e([elapsed time, ms], [logical reads])
order by
 e.[elapsed time, ms];
go

drop table dbo.t;
go


Без индекса:descriptionelapsed time, mslogical readsgroup by + count(distinct)480,5788694dense_rank() + max()3563,02262054797row_number() + count()3581,25242054926

С индексом:descriptionelapsed time, mslogical readsgroup by + count(distinct)310,23984474row_number() + count()2131,19342051463dense_rank() + max()2195,32342051484
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
17.08.2021, 06:39
    #40091128
inevity
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Оконный DISTINCT COUNT
invm,
Вот это я понимаю, уровень! Благодарю!
...
Рейтинг: 0 / 0
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Оконный DISTINCT COUNT / 11 сообщений из 11, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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