【问题标题】:Sql server combine multiple data sets without duplicate datasql server 合并多个数据集不重复数据
【发布时间】:2012-09-23 09:26:24
【问题描述】:

给定三个表 Ta、Tb、Tc:

Ta(ID, Field1)
Tb(ID, Field2)
Tc(ID, Field3)

给定数据示例:

Ta
ID Field1
---------
1  A
1  B

Tb
ID Field2
---------
1  C
1  D
2  E

Tc
ID Field3
---------
1  F
2  G
2  H

问题: 如何加入这些数据以返回:

ID Field1 Field2 Field3
-----------------------
1  A      C      F
1  B      D      NULL
2  NULL   E      G
2  NULL   NULL   H

我认为我可以通过外部连接来实现这一点,但似乎并非如此。分组的顺序并不重要,只要我带回没有重复行的所有信息即可。

只是为了澄清。只要结果集以最少的行数返回所有数据,我并不介意我得到哪种组合。这是我正在尝试做的一个更现实的例子:

给定一个人,叫他约翰。他有两个电话号码和三个电子邮件地址:

PID  Email
---------
John john@test.com
John john@mail.com
John john@john.com

PID  Tel
--------
John 011
John 022

我想回来:

PID  Email         Tel
----------------------
John john@test.com 011
John john@mail.com 022
John john@john.com NULL

【问题讨论】:

  • 为什么最后一行分配给ID = 3?
  • 1 A D F 怎么样?还有1 B C F?为什么不归还那些,它们显然在你的归还中丢失了,不是吗?在你回应之前,停下来思考一下,也许你会意识到你的需求的真正问题......
  • 您的组合似乎取决于原始表中行的顺序。在 SQL(和 SQL Server)中,未指定表中行的顺序。您是否有用于确定排序的行号或标识列或日期或其他内容?
  • “我不在乎我是否得到2 NULL E H2 NULL NULL G 或2 NULL E G2 NULL NULL H”。我想我需要更多的咖啡,因为我完全被这个要求弄糊涂了。
  • @LittleBobbyTables。这是一个数据提取。我只需要在尽可能少的行中查看相关数据。它们的排序方式无关紧要。除了 ID 之外,它们是无关的。

标签: sql sql-server outer-join


【解决方案1】:

您可以通过以下方式接近:

select coalesce(ta.id, tb.id, tc.id), ta.field1, tb.field2, tc.field3
from (select ta.*, row_number() over (partition by id order by (select NULL)) as seqnum
      from ta
     ) ta full outer join
     (select tb.*, row_number() over (partition by id order by (select NULL)) as seqnum
      from tb
     ) tb
     on ta.id = tb.id and
        ta.seqnum = tb.seqnum
     (select tc.*, row_number() over (partition by id order by (select NULL)) as seqnum
      from tc
     ) tc
     on coalesce(ta.id, tb.id) = tc.id and
        coalesce(ta.seqnum, tb.seqnum) = tc.seqnum
group by coalesce(ta.id, tb.id, tc.id),
         coalesce(ta.seqnum, tb.seqnum, tc.seqnum)
order by 1, 2

正如我所说,不过,在我的评论中,不能保证表中行的顺序,因此这些可能不会按照您期望的顺序出现。使用您的示例数据,您可以使用:

over (partition by id order by field<n>)

如果字段定义了排序

【讨论】:

    【解决方案2】:

    这是一种替代方法,使用 CTE 和联合,使用 MIN 排除空值。它不能保证顺序,但既然你说你不在乎,只要 ID 都在场。

    SQL 小提琴here

    WITH TaRanked AS
    (
      SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Field1) as Rnk, ID, Field1
      FROM Ta
    ),
    TbRanked AS
    (
      SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Field2) as Rnk, ID, Field2
      FROM Tb
    ),
    TcRanked AS
    (
      SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Field3) as Rnk, ID, Field3
      FROM Tc
    ),
    TUnion AS
    (
        SELECT Rnk, ID, Field1, NULL AS Field2, NULL AS Field3 
            FROM TaRanked 
        UNION ALL
        SELECT Rnk, ID, NULL, Field2, NULL 
            FROM TbRanked 
        UNION ALL
        SELECT Rnk, ID, NULL, NULL, Field3 
            FROM TcRanked 
    )
    SELECT ID, MIN(Field1), MIN(Field2), MIN(Field3)
      FROM TUnion
      GROUP BY ID, Rnk
      ORDER BY ID, Rnk
    

    结果是

    1   A       C       F
    1   B       D       (null)
    2   (null)  E       G
    2   (null)  (null)  H
    

    【讨论】:

    • 我没有机会尝试这个,因为 Gordon Linoff 的回答对我有用。也就是说,这似乎做同样的事情,但以一种更具可读性的方式(+1)。我会让选民从这里拿走! :)
    • Gordon 是正确的 - 需要分区来保证最少的行。这也设置了所需的顺序。我已经更新了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-15
    • 2018-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多