powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / рекурсия с дублями
15 сообщений из 15, страница 1 из 1
рекурсия с дублями
    #39929357
Фотография a_voronin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Господа, под (+) найдете скрипт, который составляет из parent-child дерево с помощью рекурсии. Зацикливания в скрипте отсечены.

Скрипт выдает множество цепочек, среди которых встречаются дубли. Дублем считается цепочка, которая может быть преобразована в другую цепочку перестановкой элементов.

-2-1-
-1-2-

-1-4-5-2-3-
-1-5-4-2-3-
-1-3-5-2-4-

Элементы в цепочке уникальны.

Кто сможет предложить алгоритм устранения дублей?

Программа минимум, дубли устранить.

Программа максимум сделать это оптимальным по времени. Чтобы на 100000 - 500000 цепочках отрабатывала за разумное время. В скрипте найдете закомментированные строки, которые позволят увеличить число вариантов.

-- много вариантов
-- средне вариантов
-- мало вариантов

Обращаю внимание, что условие "следующий элемент больше предыдущего" может не дать ожидаемого результата, так как отбросит некоторые варианты. -3-1-2- -3-2-1 Здесь нет полного перебора всех вариантов.

Код: 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.
IF OBJECT_ID('tempdb..#T') IS NOT NULL 
DROP TABLE #T;

WITH A AS 
(	
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM master..spt_values
), B AS 
(
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
	FROM A A1, A A2
)


SELECT PARENT, CHILD
INTO #T 
FROM 
(
	SELECT 
		PARENT, CHILD
	FROM 
	(
		SELECT DISTINCT 
			--RN, RN % 9 + 1 AS PARENT, RN % 11 + 1 AS CHILD -- много вариантов 
			--RN, RN % 7 + 1 AS PARENT, RN % 9 + 1 AS CHILD -- средне вариантов 
			RN % 5 + 1 AS PARENT, RN % 7 + 1 AS CHILD -- мало вариантов 
		FROM B
	) C
	WHERE CHILD <> PARENT
) D
GROUP BY PARENT, CHILD
;

WITH T AS 
(
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('-', PARENT, '-', CHILD, '-') AS VARCHAR(1024))
	FROM #T S0
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, S2.CHILD, '-') AS VARCHAR(1024)) 
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	WHERE CHARINDEX(CONCAT('-', S2.CHILD, '-'), S1.CHAIN) = 0
)
SELECT TOP 100000 t.* FROM T t
ORDER BY Start, Parent, Child

--Необходимо исключить повторы 
/* 
-2-1-
-1-2-

-1-4-5-2-3-
-1-5-4-2-3-
-1-3-5-2-4-
*/

...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929361
msLex
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
после получения ваших цепочек, отсортируйте элементы в них, и уже по отсортированным цепочкам уникализируйте.
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929368
Фотография a_voronin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
msLex
после получения ваших цепочек, отсортируйте элементы в них, и уже по отсортированным цепочкам уникализируйте.


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

1-4-5-2-3
1-5-4-2-3
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929371
msLex
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
a_voronin
msLex
после получения ваших цепочек, отсортируйте элементы в них, и уже по отсортированным цепочкам уникализируйте.


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

1-4-5-2-3
1-5-4-2-3


значит, собирайте пересортированную цепочку сразу, т.е. храните два "path" нормальный и отсортированный.

Единственное, в рекурсивном CTE этого сделать, скорее всего, не удастся, там distinct-ы запрещены.
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929373
andy st
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
a_voronin
msLex
после получения ваших цепочек, отсортируйте элементы в них, и уже по отсортированным цепочкам уникализируйте.


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

ванговать что ли?
a_voronin

1-4-5-2-3
1-5-4-2-3

а какая этих строк является единственной правильной?
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929374
msLex
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andy st
a_voronin
пропущено...


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

ванговать что ли?
a_voronin

1-4-5-2-3
1-5-4-2-3

а какая этих строк является единственной правильной?

ИМХО, любая, если в результате нужен неупорядоченный список элементов.
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929375
Фотография court
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
a_voronin
Программа минимум

Код: 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.
IF OBJECT_ID('tempdb..#T') IS NOT NULL 
DROP TABLE #T;

WITH A AS 
(	
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM master..spt_values
), B AS 
(
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
	FROM A A1, A A2
)


SELECT PARENT, CHILD
INTO #T 
FROM 
(
	SELECT 
		PARENT, CHILD
	FROM 
	(
		SELECT DISTINCT 
			--RN, RN % 9 + 1 AS PARENT, RN % 11 + 1 AS CHILD -- много вариантов 
			--RN, RN % 7 + 1 AS PARENT, RN % 9 + 1 AS CHILD -- средне вариантов 
			RN % 5 + 1 AS PARENT, RN % 7 + 1 AS CHILD -- мало вариантов 
		FROM B
	) C
	WHERE CHILD <> PARENT
) D
GROUP BY PARENT, CHILD
;

WITH T AS 
(
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('-', PARENT, '-', CHILD, '-') AS VARCHAR(1024)),
		lvl	  =1
	FROM #T S0
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, S2.CHILD, '-') AS VARCHAR(1024)),
		lvl   =lvl+1
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	WHERE CHARINDEX(CONCAT('-', S2.CHILD, '-'), S1.CHAIN) = 0
)


Код: sql
1.
2.
3.
4.
SELECT /*TOP 100000*/ t.* FROM T t
where not exists (select 1 from T t1 where t.lvl = t1.lvl and  t.CHAIN > t1.CHAIN  
                   and not exists (select value from string_split(t.CHAIN,'-') except select value from string_split(t1.CHAIN,'-') ))
ORDER BY lvl, Start, Parent, Child
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929381
msLex
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
a_voronin,

а вот этих элементов (1,2,3 и т.д.) много?

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

В реальности 3000+ и они от 100000 до 999999
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929413
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.
IF OBJECT_ID('tempdb..#T') IS NOT NULL 
DROP TABLE #T;

WITH A AS 
(	
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM master..spt_values
), B AS 
(
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
	FROM A A1, A A2
)


SELECT PARENT, CHILD
INTO #T 
FROM 
(
	SELECT 
		PARENT, CHILD
	FROM 
	(
		SELECT DISTINCT 
			--RN, RN % 9 + 1 AS PARENT, RN % 11 + 1 AS CHILD -- много вариантов 
			--RN, RN % 7 + 1 AS PARENT, RN % 9 + 1 AS CHILD -- средне вариантов 
			RN % 5 + 1 AS PARENT, RN % 7 + 1 AS CHILD -- мало вариантов 
		FROM B
	) C
	WHERE CHILD <> PARENT
) D
GROUP BY PARENT, CHILD
;

WITH T AS 
(
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('<a>', PARENT, '</a><a>', CHILD, '</a>') AS VARCHAR(1024))
	FROM #T S0
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, '<a>', S2.CHILD, '</a>') AS VARCHAR(1024)) 
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	WHERE CHARINDEX(CONCAT('<a>', S2.CHILD, '</a>'), S1.CHAIN) = 0
),
x as
(
 SELECT --TOP 100000
  t.*,
  row_number() over (partition by a.chain_ordered order by 1/0) as rn
 FROM T t cross apply
 (select cast(cast(CHAIN as xml).query('for $a in a order by xs:integer($a/text()[1]) return concat("(", $a/text()[1], ")")') as varchar(8000))) a(chain_ordered)
)
select
 Start, Parent, Child, CHAIN
from
 x
where
 rn = 1
ORDER BY Start, Parent, Child

...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929431
nullin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
msLex
a_voronin
пропущено...

Единственное, в рекурсивном CTE этого сделать, скорее всего, не удастся, там distinct-ы запрещены.

А что мешает в рекурсивной части row_number() сделать и отсечь не нужное на следующем уровне вложенности по rn = 1?

А если после рекурсии отсечь, то можно и так:
Код: sql
1.
2.
3.
4.
5.
6.
7.
select top(1) with ties c.z
  from (select b.z, string_agg(b.v, ' ') within group(order by b.v) h
          from (select a.z, value v
                  from A a
                 cross apply string_split(a.z, '-')) b
         group by b.z) c
 order by row_number() over(partition by c.h order by c.z)


Хотя вариант на exists думаю побыстрее будет.
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929439
Фотография court
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
invm ,

а если сразу в СТЕ "сортировать" XML и "фильтровать" по нему ?

Не понял, только пока, почему в таком случае
(82 rows affected)
вместо правильных
(88 rows affected)

Код: 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.
IF OBJECT_ID('tempdb..#T') IS NOT NULL 
DROP TABLE #T;

WITH A AS 
(	
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM master..spt_values
), B AS 
(
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
	FROM A A1, A A2
)


SELECT PARENT, CHILD
INTO #T 
FROM 
(
	SELECT 
		PARENT, CHILD
	FROM 
	(
		SELECT DISTINCT 
			--RN, RN % 9 + 1 AS PARENT, RN % 11 + 1 AS CHILD -- много вариантов 
			--RN, RN % 7 + 1 AS PARENT, RN % 9 + 1 AS CHILD -- средне вариантов 
			RN % 5 + 1 AS PARENT, RN % 7 + 1 AS CHILD -- мало вариантов 
		FROM B
	) C
	WHERE CHILD <> PARENT
) D
GROUP BY PARENT, CHILD
;


Код: 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.
WITH T AS 
(
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = a.CHAIN,
		b.chain_ordered
	FROM #T S0
	cross apply (select CAST(CONCAT('<a>', PARENT, '</a><a>', CHILD, '</a>') AS VARCHAR(1024)) as CHAIN) a
	cross apply (select cast(cast(a.CHAIN as xml).query('for $a in a order by xs:integer($a/text()[1]) return concat("(", $a/text()[1], ")")') as varchar(8000))) b(chain_ordered)
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = a.CHAIN,
		b.chain_ordered
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	cross apply (select CAST(CONCAT(S1.CHAIN, '<a>', S2.CHILD, '</a>') AS VARCHAR(1024)) as CHAIN) a
	cross apply (select cast(cast(a.CHAIN as xml).query('for $a in a order by xs:integer($a/text()[1]) return concat("(", $a/text()[1], ")")') as varchar(8000))) b(chain_ordered)
	WHERE CHARINDEX(CONCAT('<a>', S2.CHILD, '</a>'), S1.CHAIN) = 0
		and (CHARINDEX(S1.chain_ordered, b.chain_ordered) = 0)
)
select * from T
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929484
invm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
court
а если сразу в СТЕ "сортировать" XML и "фильтровать" по нему ?
Если вы об избавлении от фильтрации результата CTE, то не выйдет - в рекурсивной части CTE доступна только текущая строка якорной.
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39929494
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.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
use tempdb;
set ansi_nulls, quoted_identifier, xact_abort on;
go

IF OBJECT_ID('tempdb..#T') IS NOT NULL 
DROP TABLE #T;

WITH A AS 
(	
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM master..spt_values
), B AS 
(
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
	FROM A A1, A A2
)
SELECT PARENT, CHILD
INTO #T 
FROM 
(
	SELECT 
		PARENT, CHILD
	FROM 
	(
		SELECT DISTINCT 
			--RN, RN % 9 + 1 AS PARENT, RN % 11 + 1 AS CHILD -- много вариантов 
			--RN, RN % 7 + 1 AS PARENT, RN % 9 + 1 AS CHILD -- средне вариантов 
			RN % 5 + 1 AS PARENT, RN % 7 + 1 AS CHILD -- мало вариантов 
		FROM B
	) C
	WHERE CHILD <> PARENT
) D
GROUP BY PARENT, CHILD;
go

create function dbo.fnSetBit
(
 @b binary(8000),
 @bit int
)
returns table
as
return (
 select
  cast(stuff(@b, a.[byte], 1, c.b) as binary(8000)) as result
 from
  (select @bit / 8 + 1, @bit % 8) a([byte], [bit]) cross apply
  (select substring(@b, a.[byte], 1)) b(v) cross apply
  (select cast(cast(b.v as tinyint) | case a.bit when 0 then 1 when 1 then 2 when 2 then 4 when 3 then 8 when 4 then 16 when 0 then 1 when 5 then 32 when 6 then 64 when 7 then 128 end as binary(1))) c(b)
);
go

create procedure [filtering by xml]
as
begin

 declare  @Start int, @Parent int, @Child int, @Chain varchar(8000);

 WITH T AS 
 (
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('<a>', PARENT, '</a><a>', CHILD, '</a>') AS VARCHAR(8000))
	FROM #T S0
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, '<a>', S2.CHILD, '</a>') AS VARCHAR(8000)) 
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	WHERE CHARINDEX(CONCAT('<a>', S2.CHILD, '</a>'), S1.CHAIN) = 0
 ),
 x as
 (
  SELECT
   t.*,
   row_number() over (partition by a.chain_ordered order by 1/0) as rn
  FROM T t cross apply
  (select cast(cast(CHAIN as xml).query('for $a in a order by xs:integer($a/text()[1]) return concat("(", $a/text()[1], ")")') as varchar(8000))) a(chain_ordered)
 )
 select
  @Start = Start, @Parent = Parent, @Child = Child, @Chain = CHAIN
 from
  x
 where
  rn = 1
 option
  (maxdop 1);
end;
go

create procedure [filtering by bitmask]
as
begin

 declare  @Start int, @Parent int, @Child int, @Chain varchar(8000);

 WITH T AS 
 (
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('-', PARENT, '-', CHILD, '-') AS VARCHAR(1024)),
        b.result as bits
	FROM #T S0 cross apply dbo.fnSetBit(cast(0 as binary(8000)), PARENT) b
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, S2.CHILD, '-') AS VARCHAR(1024)),
        b.result
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
    cross apply dbo.fnSetBit(s1.bits, S2.CHILD) b
	WHERE CHARINDEX(CONCAT('-', S2.CHILD, '-'), S1.CHAIN) = 0
 ),
 x as
 (
  SELECT
   t.*,
   row_number() over (partition by t.bits order by 1/0) as rn
  FROM T t
 )
 select
  @Start = Start, @Parent = Parent, @Child = Child, @Chain = CHAIN
 from
  x
 where
  rn = 1
 option
  (maxdop 1);
end;
go

create procedure [filtering by exists]
as
begin

 declare  @Start int, @Parent int, @Child int, @Chain varchar(8000);

 WITH T AS 
 (
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('-', PARENT, '-', CHILD, '-') AS VARCHAR(1024)),
		lvl	  =1
	FROM #T S0
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, S2.CHILD, '-') AS VARCHAR(1024)),
        lvl = lvl + 1
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	WHERE CHARINDEX(CONCAT('-', S2.CHILD, '-'), S1.CHAIN) = 0
 )
 select
  @Start = Start, @Parent = Parent, @Child = Child, @Chain = CHAIN
 from
  T
 where
  not exists (select 1 from T t1 where t.lvl = t1.lvl and  t.CHAIN > t1.CHAIN  
                   and not exists (select value from string_split(t.CHAIN,'-') except select value from string_split(t1.CHAIN,'-') ))
 option
  (maxdop 1);
end;
go

declare @c int = 5
while @c > 0
 begin
  exec dbo.[filtering by xml];
  exec dbo.[filtering by bitmask];
  exec dbo.[filtering by exists];

  set @c -= 1;
 end;

select
 p.name, cast(ps.total_elapsed_time as float) / ps.execution_count / 1000
from
 sys.dm_exec_procedure_stats ps join
 (values (N'filtering by xml'), (N'filtering by bitmask'), (N'filtering by exists')) p(name) on ps.database_id = db_id() and ps.object_id = object_id(p.name, 'P')
order by
 2;
go

drop function dbo.fnSetBit;
drop procedure dbo.[filtering by xml], dbo.[filtering by bitmask], dbo.[filtering by exists];
go

namefiltering by bitmask119,032filtering by xml242,1514filtering by exists365,9298
...
Рейтинг: 0 / 0
рекурсия с дублями
    #39930159
Фотография court
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
invm
court
а если сразу в СТЕ "сортировать" XML и "фильтровать" по нему ?
Если вы об избавлении от фильтрации результата CTE, то не выйдет - в рекурсивной части CTE доступна только текущая строка якорной.
да, с рекурсией так не получается ...
Тогда цикл
Код: 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.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
use tempdb;
set ansi_nulls, quoted_identifier, xact_abort on;
go

IF OBJECT_ID('tempdb..#T') IS NOT NULL 
DROP TABLE #T;

WITH A AS 
(	
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM master..spt_values
), B AS 
(
	SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
	FROM A A1, A A2
)
SELECT PARENT, CHILD
INTO #T 
FROM 
(
	SELECT 
		PARENT, CHILD
	FROM 
	(
		SELECT DISTINCT 
			--RN, RN % 9 + 1 AS PARENT, RN % 11 + 1 AS CHILD -- много вариантов 
			--RN, RN % 7 + 1 AS PARENT, RN % 9 + 1 AS CHILD -- средне вариантов 
			RN % 5 + 1 AS PARENT, RN % 7 + 1 AS CHILD -- мало вариантов 
		FROM B
	) C
	WHERE CHILD <> PARENT
) D
GROUP BY PARENT, CHILD;
go

create function dbo.fnSetBit
(
 @b binary(8000),
 @bit int
)
returns table
as
return (
 select
  cast(stuff(@b, a.[byte], 1, c.b) as binary(8000)) as result
 from
  (select @bit / 8 + 1, @bit % 8) a([byte], [bit]) cross apply
  (select substring(@b, a.[byte], 1)) b(v) cross apply
  (select cast(cast(b.v as tinyint) | case a.bit when 0 then 1 when 1 then 2 when 2 then 4 when 3 then 8 when 4 then 16 when 0 then 1 when 5 then 32 when 6 then 64 when 7 then 128 end as binary(1))) c(b)
);
go

create procedure [filtering by xml]
as
begin

 declare  @Start int, @Parent int, @Child int, @Chain varchar(8000);

 WITH T AS 
 (
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('<a>', PARENT, '</a><a>', CHILD, '</a>') AS VARCHAR(8000))
	FROM #T S0
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, '<a>', S2.CHILD, '</a>') AS VARCHAR(8000)) 
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	WHERE CHARINDEX(CONCAT('<a>', S2.CHILD, '</a>'), S1.CHAIN) = 0
 ),
 x as
 (
  SELECT
   t.*,
   row_number() over (partition by a.chain_ordered order by 1/0) as rn
  FROM T t cross apply
  (select cast(cast(CHAIN as xml).query('for $a in a order by xs:integer($a/text()[1]) return concat("(", $a/text()[1], ")")') as varchar(8000))) a(chain_ordered)
 )
 select
  @Start = Start, @Parent = Parent, @Child = Child, @Chain = CHAIN
 from
  x
 where
  rn = 1
 option
  (maxdop 1);
end;
go

create procedure [filtering by bitmask]
as
begin

 declare  @Start int, @Parent int, @Child int, @Chain varchar(8000);

 WITH T AS 
 (
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('-', PARENT, '-', CHILD, '-') AS VARCHAR(1024)),
        b.result as bits
	FROM #T S0 cross apply dbo.fnSetBit(cast(0 as binary(8000)), PARENT) b
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, S2.CHILD, '-') AS VARCHAR(1024)),
        b.result
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
    cross apply dbo.fnSetBit(s1.bits, S2.CHILD) b
	WHERE CHARINDEX(CONCAT('-', S2.CHILD, '-'), S1.CHAIN) = 0
 ),
 x as
 (
  SELECT
   t.*,
   row_number() over (partition by t.bits order by 1/0) as rn
  FROM T t
 )
 select
  @Start = Start, @Parent = Parent, @Child = Child, @Chain = CHAIN
 from
  x
 where
  rn = 1
 option
  (maxdop 1);
end;
go

create procedure [filtering by exists]
as
begin

 declare  @Start int, @Parent int, @Child int, @Chain varchar(8000);

 WITH T AS 
 (
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = CAST(CONCAT('-', PARENT, '-', CHILD, '-') AS VARCHAR(1024)),
		lvl	  =1
	FROM #T S0
	UNION ALL 
	SELECT 
		S1.Start, 
		S2.PARENT AS Parent, 
		S2.CHILD AS Child, 
		CHAIN = CAST(CONCAT(S1.CHAIN, S2.CHILD, '-') AS VARCHAR(1024)),
        lvl = lvl + 1
	FROM T S1
	INNER JOIN #T S2 ON S1.Child = S2.PARENT
	WHERE CHARINDEX(CONCAT('-', S2.CHILD, '-'), S1.CHAIN) = 0
 )
 select
  @Start = Start, @Parent = Parent, @Child = Child, @Chain = CHAIN
 from
  T
 where
  not exists (select 1 from T t1 where t.lvl = t1.lvl and  t.CHAIN > t1.CHAIN  
                   and not exists (select value from string_split(t.CHAIN,'-') except select value from string_split(t1.CHAIN,'-') ))
 option
  (maxdop 1);
end;
go

create procedure [filtering in while]
as
begin

	declare  @Start int, @Parent int, @Child int, @Chain varchar(max);
 
	declare @lvl integer =0;
	create table #Result (Start integer, Parent integer, Child integer, CHAIN varchar(1024), LVL integer, chain_ordered varchar(1024));

	---------------------------------------------------------------------------------------
	set @lvl = @lvl + 1;
	--
	insert into #Result 
		(Start, Parent, Child, CHAIN, LVL, chain_ordered)
	SELECT 
		PARENT AS Start, 
		PARENT AS Parent, 
		CHILD AS Child, 
		CHAIN = a.CHAIN,
		@lvl,
		b.chain_ordered
	FROM #T S0
	cross apply (select CAST(CONCAT('<a>', S0.PARENT, '</a><a>', S0.CHILD, '</a>') AS VARCHAR(1024)) as CHAIN) a
	cross apply (select cast(cast(a.CHAIN as xml).query('for $a in a order by xs:integer($a/text()[1]) return concat("(", $a/text()[1], ")")') as varchar(8000))) b(chain_ordered);

	--
	with cte as (
		select row_number()over(partition by /*Child,*/ chain_ordered order by CHAIN) as rn from #Result where LVL = @lvl) -- partition by Child, chain_ordered если граф неполносвязный
	delete from cte where rn > 1;

	--	
	while 1=1
	begin
		set @lvl = @lvl + 1;
		--
		insert into #Result 
			(Start, Parent, Child, CHAIN, LVL, chain_ordered)
		SELECT 
			S1.Start, 
			S2.PARENT AS Parent, 
			S2.CHILD AS Child, 
			CHAIN = a.CHAIN,
			@lvl,
			b.chain_ordered
		FROM #Result S1
		INNER JOIN #T S2 ON S1.Child = S2.PARENT
		cross apply (select CAST(CONCAT(S1.CHAIN, '<a>', S2.CHILD, '</a>') AS VARCHAR(1024)) as CHAIN) a
		cross apply (select cast(cast(a.CHAIN as xml).query('for $a in a order by xs:integer($a/text()[1]) return concat("(", $a/text()[1], ")")') as varchar(8000))) b(chain_ordered)
		WHERE S1.LVL = @lvl-1 and CHARINDEX(CONCAT('<a>', S2.CHILD, '</a>'), S1.CHAIN) = 0
		--
		if @@ROWCOUNT = 0 break;
		--
		with cte as (
			select row_number()over(partition by /*Child,*/ chain_ordered order by CHAIN) as rn from #Result where LVL = @lvl) -- partition by Child, chain_ordered если граф неполносвязный
		delete from cte where rn > 1;
	end

	--
	;with cte as (
		select row_number()over(partition by chain_ordered order by CHAIN) as rn from #Result where LVL = @lvl)
	delete from cte where rn > 1;

	--	
	select 
		@Start = Start, @Parent = Parent, @Child = Child, @Chain = CHAIN 
	from 
		#Result
	option 
		(maxdop 1);
end;
go

declare @c int = 5
while @c > 0
 begin
  exec dbo.[filtering by xml];
  exec dbo.[filtering by bitmask];
  exec dbo.[filtering by exists];
  exec dbo.[filtering in while];
  set @c -= 1;
 end;

select
 p.name, cast(ps.total_elapsed_time as float) / ps.execution_count / 1000
from
 sys.dm_exec_procedure_stats ps join
 (values (N'filtering by xml'), (N'filtering by bitmask'), (N'filtering by exists'), (N'filtering in while')) p(name) on ps.database_id = db_id() and ps.object_id = object_id(p.name, 'P')
order by
 2;
go

drop function dbo.fnSetBit;
drop procedure dbo.[filtering by xml], dbo.[filtering by bitmask], dbo.[filtering by exists], dbo.[filtering in while];
go


name(No column name)filtering in while23,9758filtering by bitmask111,9324filtering by xml208,8516filtering by exists276,851
...
Рейтинг: 0 / 0
15 сообщений из 15, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / рекурсия с дублями
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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