元组的 SQL 去重列表

Posted

技术标签:

【中文标题】元组的 SQL 去重列表【英文标题】:SQL Deduplicate List of Tuples 【发布时间】:2017-11-14 19:27:34 【问题描述】:

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

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

每一行代表一个sql表中两行的ID,它们被认为是彼此重复的。

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

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

期望的结果:

    最低 ID 应保留为主 受骗者的所有后续受骗者都应映射回同一(最低 ID)主人

对于上述情况,所需的输出看起来像这样(但不必对列进行排序):

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

我发现很难解释这个问题,所以我的谷歌搜索没有返回太多。我在想一定有一个算法可以遍历这样的元组列表并发现重复。

任何帮助表示赞赏!

编辑:我已经修改了示例表以更好地解释它们的内容可能是什么样子。

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

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

据我所见,问题看起来是递归的,我认为 LukStorms 是在正确的轨道上,但我无法完全弄清楚

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

【问题讨论】:

你能更好地解释你的逻辑吗?原始表中的结果集中显示的 2 和 3 之间的关系的性质是什么? 当然,3和6是骗子,6和7是骗子,7和2是骗子。保持集合 (2) 的最低 id,ID 的 3、6 和 7 都是 2 的骗子。 【参考方案1】:

试试这个。使用 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

更新:

在您对更多行进行编辑后,尽管我不确定这是否是最佳解决方案,但下面的内容仍然有效。逻辑从第一个 master 副本开始(因为数据按 master 排序),如果副本存在于第二列且第一列不等于当前 master,则取相同的 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

【讨论】:

这里假设它是一个链,请看我的编辑 @artm 您可能想查看这些 row_number() 的顺序。在第一个 CTE 中使用表时,(select 1) 不会给出正确的顺序,因此结果可能会有所不同。 @LukStorms 你说得对,谢谢。我把它改成由主人订购。 谢谢你们!!我已经编辑了原始帖子并标记了答案` @SeanMissingham 不用担心,很乐意提供帮助。【参考方案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;

【讨论】:

我喜欢你用 RCTE 去哪里,但这个过程似乎假设整个事情是一个链条,并且它是循环的,因为它在原始对反转的情况下关闭。如果您取出底部的 7,2 对,它不再起作用,但结果应该仍然相同。如果您愿意,请查看修改后的示例:) @SeanMissingham 确实,存在基于先前测试数据的假设。不过,不会将其视为“一条链”,它更像是获得分支。答案已更新。【参考方案3】:

聚会迟到了,但你似乎想要找到不连贯的场景。 如果您关心效率,有一个非常快的算法,它涉及称为UnionFind 的数据结构。似乎比排序还要快……

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

【讨论】:

以上是关于元组的 SQL 去重列表的主要内容,如果未能解决你的问题,请参考以下文章

Python的列表和元组的区别

1-面试题:python

使用参数列表或元组在 pandas 中读取 SQL

2017.7.17 列表去重,列表元组,集合

Python:元组列表:比较所有元组并检索元组的元素不等于任何其他元组的元组

Python 2.7 学习笔记 元组的使用