SQL Deduplicate元组列表

时间:2017-06-13 06:57:45

标签: sql sql-server recursion duplicates

我有一个包含两列ID的表,如下所示:

╔════════╦══════╗
║ Master ║ Dupe ║
╠════════╬══════╣
║ 2      ║ 7    ║
║ 3      ║ 6    ║
║ 6      ║ 7    ║
║ 20     ║ 25   ║
║ 75     ║ 25   ║
╚════════╩══════╝

每行表示sql表中两行的ID,这两行被认为是彼此重复的。

此表可以包含数千个条目,但不保证Master列以外的数据按照图示的升序排序。任何一列都可以包含与另一列相同的ID,可能针对不同或相同的合作伙伴ID。再次 - 没有保证。

从这张表中我想获得Master的索引及其所有可能的欺骗。如下图所示。

期望的结果:

  1. 最低ID应保留为主
  2. 欺骗的所有后续欺骗应该映射回相同(最低ID)的主人
  3. 对于上面的内容,所需的输出看起来像是这样(但是列不必排序):

    ╔════════╦══════╗
    ║ Master ║ Dupe ║
    ╠════════╬══════╣
    ║ 2      ║ 3    ║
    ║ 2      ║ 6    ║
    ║ 2      ║ 7    ║
    ║ 20     ║ 25   ║
    ║ 20     ║ 75   ║
    ╚════════╩══════╝
    

    我发现很难解释这个问题所以我的谷歌搜索没有太多回复。我认为必须有一个算法来迭代这样的元组列表并发现重复。

    任何帮助表示赞赏!

    编辑:我修改了示例表,以更好地解释其内容可能是什么样的。

    需要考虑的一些注意事项,

    • 无法保证链条。它可能都是一个大链条,许多小链条或根本没有。
    • 无法保证所有对在表中的其他位置以相反的顺序出现

    从我所看到的,问题看起来是递归的,我认为LukStorms是在正确的轨道但我无法弄明白

    回答:虽然@artm和@LukStorms下面的两个解决方案似乎都有效,但我发现后者更简洁,更易读。谢谢你们俩!一个棘手问题的神奇帮助。我只希望我能给你答案

3 个答案:

答案 0 :(得分:4)

试试这个。使用CTE从表中获取master的最小值并交叉连接到表中的所有其他值。

;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

<强>更新

使用更多行进行编辑后,下面有效,但我不确定它是否是最佳解决方案。逻辑从第一个主设备开始(因为数据按主数据排序),如果第一列不等于当前主设备的第二列存在欺骗,则采用相同的主设备,否则采用下一个主设备。很难解释,其他人可能会找到一个更容易的解决方案。

;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

答案 1 :(得分:2)

这是一个使用递归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;

答案 2 :(得分:1)

派对迟到了,但你似乎想要的是找到脱离的集合。 如果你关心效率,那么有一个非常快速的算法,它涉及名为UnionFind的数据结构。它似乎比甚至排序更快......

谷歌搜索SQL实现,我是领导there