powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Оконный DISTINCT COUNT
11 сообщений из 11, страница 1 из 1
Оконный DISTINCT COUNT
    #38901343
Фотография a_voronin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Мне потребовалось посчитать оконный 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
Оконный DISTINCT COUNT
    #38902067
Фотография denvio
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: 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
Оконный DISTINCT COUNT
    #38902438
Фотография a_voronin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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
Оконный DISTINCT COUNT
    #38904339
Фотография denvio
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
без параллелизма у меня вот такая картина.
Какому методу отдать предпочтение, вопрос пока открыт для меня...
...
Рейтинг: 0 / 0
Оконный DISTINCT COUNT
    #38904371
0-0
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
0-0
Гость
denvio, a_voronin

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

На моём 2014-ом CASE WHEN ROW_NUMBER() проигрывает DENSE_RANK на несколько процентов по всем параметрам
...
Рейтинг: 0 / 0
Оконный DISTINCT COUNT
    #38905168
Фотография denvio
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
5 разных серверов, 3 снимка в разное время.
Чтение все время больше у ROW_NUMBER(), только вот Duration в большинстве случаев не в пользу DENSE_RANK
...
Рейтинг: 0 / 0
Оконный DISTINCT COUNT
    #38905170
Фотография denvio
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Оконный DISTINCT COUNT
    #38905171
Фотография denvio
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Оконный DISTINCT COUNT
    #38905292
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.
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
Период между сообщениями больше года.
Оконный DISTINCT COUNT
    #40091128
inevity
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
invm,
Вот это я понимаю, уровень! Благодарю!
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Оконный DISTINCT COUNT
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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