SQL: 删除自连接中的重复项
SQL: Remove duplicates in self-join
我有以下 table(称为 t1):
| id | Name |
| 1 | Charlie |
| 2 | Bob |
| 3 | Alice |
我想将 table 与其自身匹配(自连接),但只能选择尚未出现的组合。到目前为止,我有以下内容:
select * from t1 a, t1 b
where a.id != b.id
这给了我这个结果:
| a.id | a.Name | b.id | b.Name |
| 2 | Bob | 1 | Charlie |
| 3 | Alice | 1 | Charlie |
| 1 | Charlie | 2 | Bob |
| 3 | Alice | 2 | Bob |
| 1 | Charlie | 3 | Alice |
| 2 | Bob | 3 | Alice |
我只希望 id 在 table a 中出现一次,在 table b 中出现一次。期望的结果是:
| a.id | a.Name | b.id | b.Name |
| 2 | Bob | 1 | Charlie |
| 3 | Alice | 2 | Bob |
| 1 | Charlie | 3 | Alice |
但我不知道如何保证这一点。
我正在使用 SQL Server 2017。
这是我的测试 fiddle:DEMO
PS:我已经检查了 this 问题,但是在我自己的示例中,我不清楚使用 "less than" 作为比较运算符的解决方案的概念。
编辑:没有关于选择哪对的规则;这些对可以是 (2,3)、(3,1)、(1,2) 而不是我上面介绍的那些,因为我感兴趣的唯一规则是 每个 id 来自 [=] 只有一次51=] a 和从 table b, 和 a.id != b.id.
编辑2:没有逻辑可以匹配他们,请把它当作这个可能的前提来思考:
我正在为爱丽丝、鲍勃和查理牵线搭桥,就好像他们在秘密交换礼物一样。他们只能给一个人送礼,也只能给一个人送礼,不能给自己送礼。 (我认为这允许可扩展性)
OP 想匹配给每个人分配一个随机伙伴,解决方案不是完全随机的,只有 ID 是连续的才有效。但是,可以通过调用组合 random/order_by/row_number
来修复它
我的懒惰修复是:
select * from t1 a, t1 b
where a.id = b.id % ( select count(*) from t1 c) + 1
使用row_number()
。然后根据行号进行自连接。
select a.id, a.name, b.id, b.name from
(select row_number() over (order by id desc) rn, id, name from t1) a
join
(select row_number() over (order by id asc) rn, id, name from t1) b on a.rn= b.rn
这是一个选项,它使用 ROW_NUMBER
技巧将每个名称与不同的名称交错排列:
WITH cte AS (
SELECT id, Name, ROW_NUMBER() OVER (ORDER BY id) rn
FROM t1
)
SELECT
t1.Name,
t2.Name
FROM cte t1
INNER JOIN cte t2
ON (t1.rn % (SELECT COUNT(*) FROM cte)) + 1 = t2.rn;
逻辑是将行号 1 与 2、2 与 3 以及 3 与 1 匹配(我们使用模数在边缘情况下环绕)。这可确保任何名称都不会在给定列中出现多次。
这是另一种方法。
这将根据两个 ID 中较大的一个对数据进行分区并创建一个连接字符串 (larger_id,'|',smaller_id)
之后我通过检查在连接的字符串上只选择了一个值
其中 rnk=1.
with data
as (
select a.id a_id,a.name as a_name,b.name as b_name,b.id b_id
,row_number() over(partition by case when a.id>b.id then concat(a.id,'|',b.id)
else concat(b.id,'|',a.id) end
order by b.id desc)
as rnk
from t1 a
join t1 b
on a.id != b.id
)
select *
from data
where rnk=1
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=c3f82c8d21dc14899a263adacf1b31e6
我有以下 table(称为 t1):
| id | Name |
| 1 | Charlie |
| 2 | Bob |
| 3 | Alice |
我想将 table 与其自身匹配(自连接),但只能选择尚未出现的组合。到目前为止,我有以下内容:
select * from t1 a, t1 b
where a.id != b.id
这给了我这个结果:
| a.id | a.Name | b.id | b.Name |
| 2 | Bob | 1 | Charlie |
| 3 | Alice | 1 | Charlie |
| 1 | Charlie | 2 | Bob |
| 3 | Alice | 2 | Bob |
| 1 | Charlie | 3 | Alice |
| 2 | Bob | 3 | Alice |
我只希望 id 在 table a 中出现一次,在 table b 中出现一次。期望的结果是:
| a.id | a.Name | b.id | b.Name |
| 2 | Bob | 1 | Charlie |
| 3 | Alice | 2 | Bob |
| 1 | Charlie | 3 | Alice |
但我不知道如何保证这一点。
我正在使用 SQL Server 2017。
这是我的测试 fiddle:DEMO
PS:我已经检查了 this 问题,但是在我自己的示例中,我不清楚使用 "less than" 作为比较运算符的解决方案的概念。
编辑:没有关于选择哪对的规则;这些对可以是 (2,3)、(3,1)、(1,2) 而不是我上面介绍的那些,因为我感兴趣的唯一规则是 每个 id 来自 [=] 只有一次51=] a 和从 table b, 和 a.id != b.id.
编辑2:没有逻辑可以匹配他们,请把它当作这个可能的前提来思考: 我正在为爱丽丝、鲍勃和查理牵线搭桥,就好像他们在秘密交换礼物一样。他们只能给一个人送礼,也只能给一个人送礼,不能给自己送礼。 (我认为这允许可扩展性)
OP 想匹配给每个人分配一个随机伙伴,解决方案不是完全随机的,只有 ID 是连续的才有效。但是,可以通过调用组合 random/order_by/row_number
来修复它我的懒惰修复是:
select * from t1 a, t1 b
where a.id = b.id % ( select count(*) from t1 c) + 1
使用row_number()
。然后根据行号进行自连接。
select a.id, a.name, b.id, b.name from
(select row_number() over (order by id desc) rn, id, name from t1) a
join
(select row_number() over (order by id asc) rn, id, name from t1) b on a.rn= b.rn
这是一个选项,它使用 ROW_NUMBER
技巧将每个名称与不同的名称交错排列:
WITH cte AS (
SELECT id, Name, ROW_NUMBER() OVER (ORDER BY id) rn
FROM t1
)
SELECT
t1.Name,
t2.Name
FROM cte t1
INNER JOIN cte t2
ON (t1.rn % (SELECT COUNT(*) FROM cte)) + 1 = t2.rn;
逻辑是将行号 1 与 2、2 与 3 以及 3 与 1 匹配(我们使用模数在边缘情况下环绕)。这可确保任何名称都不会在给定列中出现多次。
这是另一种方法。
这将根据两个 ID 中较大的一个对数据进行分区并创建一个连接字符串 (larger_id,'|',smaller_id)
之后我通过检查在连接的字符串上只选择了一个值 其中 rnk=1.
with data
as (
select a.id a_id,a.name as a_name,b.name as b_name,b.id b_id
,row_number() over(partition by case when a.id>b.id then concat(a.id,'|',b.id)
else concat(b.id,'|',a.id) end
order by b.id desc)
as rnk
from t1 a
join t1 b
on a.id != b.id
)
select *
from data
where rnk=1
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=c3f82c8d21dc14899a263adacf1b31e6