Мне потребовалось 3 CTE и пара чашек кофе, но вот оно у вас ... Моя главная задача - прочитать это из комментариев
Это повторяемая задача.Будет несколько групп, и нам придется сделать это для каждой группы.Общее количество записей во всех группах может составлять миллионы.
Это не может быть повторяемой задачей, так как потребление ресурсов будет высоким, я рекомендую использовать его один раз для нормализации ваших групп и добавить логику в ваше приложение или хранимые процедуры для хранения новых данных стребуемые группы
DECLARE @table TABLE (id int not null identity, [Group] varchar(3), Member varchar(3), Address varchar(3), Phone varchar(3), Email varchar(3))
insert @table values
('G1', 'M1', 'A1', 'P1', 'E1'),
('G1', 'M2', 'A2', 'P2', 'E2'),
('G1', 'M3', 'A1', 'P3', 'E1'),
('G1', 'M4', 'A4', 'P3', 'E4'),
('G1', 'M5', 'A5', 'P5', 'E2'),
('G1', 'M6', 'A6', 'P6', 'E6'),
('G1', 'M7', 'A7', 'P6', 'E7'),
('G1', 'M8', 'A8', 'P8', 'E4'),
('G1', 'M9', 'A9', 'P9', 'E7'),
('G1', 'M10', 'A10', 'P10', 'E10');
with
/* Find all matches
id Member MatchWith
1 M1 M3
2 M2 M5
3 M3 M1
3 M3 M4 ...
*/
matches as (
SELECT t.id, t.[Group], t.Member, a.member as MatchWith
from
@table t
outer apply (
select distinct member
from @table
where member <> t.member and [group] = t.[group] and (Address = t.Address OR Phone = t.Phone OR Email = t.Email)
) a
)
/* Stuffing the matches per member
id Member AllMatches
1 M1 M1,M3
2 M2 M2,M5
3 M3 M1,M3,M4 .....
*/
, matchsummary as (
SELECT DISTINCT id, [Group], Member, STUFF((
SELECT ',' + Member FROM (
SELECT m.Member
UNION ALL
SELECT DISTINCT MatchWith
FROM matches
WHERE Member = m.Member) U
ORDER BY Member
FOR XML PATH('')
), 1, 1, '') as AllMatches
FROM matches m
)
/* Recursive CTE to find "cousins" records (M1, M3 matches on Address and Email; M3 in turn matches with M4 on Phone)
id Member AllMatches gr
1 M1 M1,M3 1
2 M2 M2,M5 2
3 M3 M1,M3,M4 1
4 M4 M3,M4,M8 1
*/
, tree as (
select *, ROW_NUMBER() over (order by id) as gr
from matchsummary where AllMatches LIKE member+'%'
/* The groups are created using the Members who are the first one in their matches
id Member AllMatches gr
1 M1 M1,M3 1
2 M2 M2,M5 2
6 M6 M6,M7 3
10 M10 M10 4
*/
union all
select s.*, t.gr
from matchsummary s
join tree t on s.Member <> t.Member and s.[Group] = t.[Group] and s.AllMatches NOT LIKE s.member+'%' and t.AllMatches like '%' + s.Member
)
select * from tree
order by id
option(maxrecursion 0)
Выход:
ID Участник группы NewGroup
1 G1 M1 1
2G1 M2 2
3 G1 M3 1
4 G1 M4 1
5 G1 M5 2
6 G1 M6 3
7G1 M7 3
8 G1 M8 1
9 G1 M9 3
10 G1 M10 4
Второй вариант
Учитывая размер вашей таблицы, я рекомендую вам использовать это, я не большой поклонник циклов, но здесь я думаю, что они того стоят, так что вам не нужно обрабатывать все ваши данные сразу,
Во-первых, вам нужно добавить новый столбец в вашей таблице для хранения новой группы, моя первая мысль была о том, что было бы лучше изменить логику вашего приложения для вычисления этой группы при необходимости.w запись вставлена, но, если подумать, вставка может привести к тому, что несколько групп станут одной, и вам, вероятно, понадобится быстрый ответ в вашем приложении.Таким образом, вы можете настроить задание на перегруппировку ваших данных так часто, как вам это необходимо. Если у вас есть поле UpdateDate в вашей таблице, вы также можете усовершенствовать это решение, используя таблицу Log, и обрабатывать только те группы, которые были изменены после их последнего выполнения.
IF OBJECT_ID('tempdb..#table') IS NOT NULL
DROP TABLE #table;
CREATE TABLE #table ([Group] varchar(3), Member varchar(3), Address varchar(3), Phone varchar(3), Email varchar(3))
INSERT #table ([Group], Member, Address, Phone, Email)
VALUES
('G1', 'M1', 'A1', 'P1', 'E1'),
('G1', 'M2', 'A2', 'P2', 'E2'),
('G1', 'M3', 'A1', 'P3', 'E1'),
('G1', 'M4', 'A4', 'P3', 'E4'),
('G1', 'M5', 'A5', 'P5', 'E2'),
('G1', 'M6', 'A6', 'P6', 'E6'),
('G1', 'M7', 'A7', 'P6', 'E7'),
('G1', 'M8', 'A8', 'P8', 'E4'),
('G1', 'M9', 'A9', 'P9', 'E7'),
('G1', 'M10', 'A10', 'P10', 'E10');
ALTER TABLE #table ADD newGroup INT
/******************************************************************
START HERE
******************************************************************/
IF OBJECT_ID('tempdb..#Groups') IS NOT NULL
DROP TABLE #Groups;
SELECT DISTINCT [Group] INTO #Groups FROM #table
DECLARE @Group VARCHAR(3)
WHILE EXISTS (SELECT 1 FROM #Groups)
BEGIN
SELECT TOP 1 @Group = [Group] FROM #Groups
UPDATE #table SET newGroup = NULL
WHERE [Group] = @Group
DECLARE @newGroup INT = 1
DECLARE @member varchar(3)
WHILE EXISTS (SELECT 1 FROM #table WHERE [Group] = @Group AND newGroup IS NULL)
BEGIN
SELECT TOP 1 @member = member FROM #table WHERE [group] = @group AND newGroup IS NULL
UPDATE #table SET newGroup = @newGroup
WHERE Member = @member
WHILE @@ROWCOUNT > 0
BEGIN
UPDATE T
SET newGroup = @newGroup
FROM #table T
WHERE [Group] = @group AND newGroup IS NULL
AND EXISTS (
SELECT 1 FROM #table
WHERE newGroup = @newGroup
AND (Address = t.Address OR Phone = t.Phone OR Email = t.Email)
)
END
SET @newGroup += 1
END
DELETE #Groups WHERE [Group] = @Group
END
SELECT * FROM #table