Список SQL дубликатов кортежей
у меня есть таблица с двумя столбцами идентификаторов, например, так:
╔════════╦══════╗
║ Master ║ Dupe ║
╠════════╬══════╣
║ 2 ║ 7 ║
║ 3 ║ 6 ║
║ 6 ║ 7 ║
║ 20 ║ 25 ║
║ 75 ║ 25 ║
╚════════╩══════╝
каждая строка представляет идентификаторы двух строк в таблице sql, которые считаются дубликатами друг друга.
эта таблица может содержать много тысяч записей, без каких-либо гарантий для данных, кроме Master
столбец сортируется в порядке возрастания, как показано на рисунке. Любой столбец может содержать тот же идентификатор, что и другой столбец, потенциально против другого или того же идентификатора партнера. И снова-нет. гарантированное обеспечение.
из этой таблицы я хотел бы получить индекс Master и всех его возможных дураков. Как показано ниже.
желаемых результатов:
- самый низкий идентификатор должен храниться как master
- все последующие дураки дурака должны сопоставляться с тем же (самым низким идентификатором) master
Для выше, желаемый результат будет выглядеть так (но столбцы не должны быть отсортированы):
╔════════╦══════╗
║ Master ║ Dupe ║
╠════════╬══════╣
║ 2 ║ 3 ║
║ 2 ║ 6 ║
║ 2 ║ 7 ║
║ 20 ║ 25 ║
║ 20 ║ 75 ║
╚════════╩══════╝
Я нахожу его трудно объяснить эту проблему, поэтому мой googling не вернулся много. Я думаю, что где-то должен быть алгоритм для итерации по списку таких кортежей и обнаружения дублирования.
любая помощь приветствуется!
EDIT: я изменил таблицы примеров, чтобы лучше объяснить, как может выглядеть их содержимое.
некоторые заметки для рассмотрения,
- нет никакой гарантии цепи. Это может быть одна большая цепь, много маленьких. или вообще ни одной.
- нет никакой гарантии, что все пары появляются в обратном порядке где-то еще в таблице
из того, что я вижу, проблема выглядит рекурсивной, я думаю, что LukStorms находится на правильном пути, но я не могу понять это
ответ: в то время как оба решения ниже от @artm и @LukStorms, похоже, работают, я нашел, что последний был немного более кратким и читаемым. Спасибо вам обоим! Фантастическая помощь на жестком вопрос. Я только хотел бы, чтобы я мог наградить ответ вам обоим
3 ответов
вот пример, который использует рекурсивный CTE для подключения этих дубликатов.
но чтобы убедиться, что дубликаты все в обоих направлениях, используется DUPES CTE.
declare @DuplicateTest table (Master int, Dupe int);
insert into @DuplicateTest (Master, Dupe) values
(3,6),(6,7),(2,7),
(20,25),(75,25);
;with DUPES as
(
select distinct Master as Dupe1, Dupe as Dupe2 from @DuplicateTest
union
select distinct Dupe, Master from @DuplicateTest
)
,RCTE as
(
select Dupe1 as Base, 0 as Level, Dupe1, Dupe2
from DUPES
union all
select r.Base, (r.Level + 1), d.Dupe1, d.Dupe2
from RCTE r
join DUPES d on (r.Dupe2 = d.Dupe1
and r.Dupe1 != d.Dupe2 -- don't loop on the reverse
and r.Base != d.Dupe2 -- don't repeat what we started from
and r.Level < 100) -- if the level gets to big it's most likely a loop
)
select min(Dupe2) as Master, Base as Dupe
from RCTE
group by Base
having Base > min(Dupe2)
order by Base;
попробуйте это. Получите min master из своей таблицы с CTE и перекрестным соединением со всеми другими значениями в таблице.
;WITH minmaster as (select MIN(MASTER) master
FROM myTable)
select distinct m.master
, i.dupe
from minmaster m
cross join (select dupe dupe from myTable union all select master from myTable) i
WHERE i.dupe <> m.master
обновление:
после редактирования с большим количеством строк это работает, хотя я не уверен, что это лучшее решение. Логика была начата с первого master dupe (так как данные сортируются по master), если dupe существует на 2-м столбце, где первый столбец не равен текущему master, то возьмите тот же master, иначе возьмите следующий мастер. Это трудно объяснить, кто-то другой, вероятно, может найти более простое решение.
;WITH myTable AS
(SELECT 2 MASTER, 7 dupe
UNION all SELECT 3, 6
UNION all SELECT 6, 7
UNION all SELECT 20, 25
UNION all SELECT 75, 25
UNION all SELECT 100, 125
UNION all SELECT 150, 300
UNION all SELECT 180, 300
)
, cte AS
(
SELECT m.master L, m.dupe R, ROW_NUMBER() OVER (ORDER BY master) rnkC
FROM myTable m
)
, cte2 AS
(
SELECT m.master L, m.dupe R, ROW_NUMBER() OVER (ORDER BY master) rnkC2
FROM myTable m
)
, cteCur AS
(
SELECT TOP 1 cte.l, cte.R, cte.rnkC
FROM cte
UNION ALL
SELECT
CASE WHEN cteCur.r IN (SELECT dupe
FROM myTable
WHERE MASTER <> cteCur.L AND dupe = cteCur.R)
THEN cteCur.L
ELSE (SELECT cte2.L
FROM cte2
WHERE cte2.rnkC2 = cteCur.rnkC + 1)
END
, CASE WHEN cteCur.r IN (SELECT dupe
FROM myTable
WHERE MASTER <> cteCur.L AND dupe = cteCur.R)
THEN (SELECT cte2.L
FROM cte2
WHERE cte2.R = cteCur.R AND cte2.L <> cteCur.L)
ELSE (SELECT cte2.R
FROM cte2
WHERE cte2.rnkC2 = cteCur.rnkC + 1)
END
, cteCur.rnkC + 1
FROM cteCur
WHERE cteCur.L IS NOT NULL
)
SELECT cteCur.L Master
, cteCur.R Dupe
FROM cteCur
WHERE L IS NOT NULL
ORDER BY L, R
приходит поздно на вечеринку, но то, что вы, кажется, хотите, чтобы найти несвязанные наборы. Если вы заботитесь об эффективности, для этого существует чрезвычайно быстрый алгоритм, и он включает в себя структуру данных под названием UnionFind. Кажется, это даже быстрее, чем сортировка...
Googling для реализаций SQL, я был ведущим здесь