【问题标题】:SQL Union but filter out Partial DuplicatesSQL联合但过滤掉部分重复
【发布时间】:2017-01-13 19:44:28
【问题描述】:

这是我所拥有的非常简化的版本。

我有 2 个数据库表 People 和 Contacts。 People是原始表,但是contacts是一个新表,用于覆盖People表中的数据(这些是实际场景中的不同数据库)

在初始加载时,所有人员记录都被复制到联系人表中,但在初始加载后,可以将新人员添加到人员或联系人表以及设置为已删除的联系人。

我希望所有人都返回,但我希望联系人表是最重要的,例如

例子。

人员表

     PersonGuid | PersonId | FirstName   | LastName
     -----------|----------|-------------|-----
1      xxxx1    | 1        | John        | Smith
2      xxxx2    | 2        | Jane        | Doe
3      xxxx3    | 3        | John        | Doe
4      xxxx4    | 4        | Alice       | James
5      xxxx5    | 5        | Claire      | Wheeler

联系人表 (Jane Doe 设置为已删除,John Doe 不存在,因为在初始导入后添加,并且已添加 Emma Green。Deleted 为 NULL 对于尚未触及的那些)

     ID         | LinkedId | FirstName   | LastName |  Deleted
     -----------|----------|-------------|----------|----------
1      xxxx1    | 1        | John        | Smith    |  NULL
2      xxxx2    | 2        | Jane        | Doe      |  1
3      xxxx3    | NULL     | Emma        | Green    |  0
4      xxxx4    | 4        | Alice       | James    |  0
5      xxxx5    | 5        | Claire      | Wheeler  |  NULL

我要返回的内容是:(所有记录,但 Jane Doe 在联系人表中设置为已删除)

     ID         | LinkedId | FirstName   | LastName |  Deleted
     -----------|----------|-------------|----------|----------
1      xxxx1    | 1        | John        | Smith    |  NULL
2      xxxx2    | 2        | Jane        | Doe      |  1
3      xxxx3    | 3        | John        | Doe      |  0
4      xxxx4    | NULL     | Emma        | Green    |  0
5      xxxx5    | 4        | Alice       | James    |  0
6      xxxx6    | 5        | Claire      | Wheeler  |  NULL

但我得到的是: (简·多伊两次)

     ID         | LinkedId | FirstName   | LastName |  Deleted
     -----------|----------|-------------|----------|----------
1      xxxx1    | 1        | John        | Smith    |  NULL
2      xxxx2    | 2        | Jane        | Doe      |  1
3      xxxx2    | 2        | Jane        | Doe      |  0
4      xxxx3    | 3        | John        | Doe      |  0
5      xxxx4    | NULL     | Emma        | Green    |  0
6      xxxx5    | 4        | Alice       | James    |  0
7      xxxx6    | 5        | Claire      | Wheeler  |  NULL

我正在尝试合并这两个表,但我不知道如何过滤它以使联系人数据覆盖人员数据但同时从两个表中获取所有数据?

Select PersonGuid as Id,
Personid,
Firstname,
LastName,
cast(0 as bit) as deleted
From DB1.People

Union

Select
Id,
LinkedId as PersonId,
FirstName,
LastName,
Deleted 
from DB2.Contacts

我尝试过搜索,但不确定要搜索什么,所以我的术语可能有误。

编辑 - 真正的查询远没有我的示例那么简单,并且 from 是每个查询上大约 6 个表的连接。我试图做一个简单的例子来说明清楚,但不确定这是否使解决方案有所不同?

【问题讨论】:

  • Jane Doe 和 John Doe 是打算拥有相同的 ID 还是拼写错误? Emma Green 和 John Doe 也有同样的问题?
  • 刚刚注意到这一点。不,它们不应该具有相同的 ID。我现在要编辑

标签: sql sql-server join union


【解决方案1】:

你能试试这个吗:

SELECT p.PersonGuid, 
    p.PersonId, 
    p.FirstName, 
    p.LastName, 
    CASE WHEN c.LinkedId IS NULL THEN 0 ELSE c.DELETED END deleted 
FROM people p
LEFT JOIN contact c ON c.LinkedId = p.PersonId
UNION
SELECT * FROM #contact a WHERE a.LinkedId IS NULL

【讨论】:

    【解决方案2】:

    我认为这可能会满足您的需求:

    select pc.*
    from (select pc.*,
                 row_number() over (partition by firstname, lastname order by priority) as seqnum
    from ((select ID, LinkedId, FirstName, LastName, Deleted, 1 as priority
                 from contacts
                ) union all
                (select PersonGuid, PersonId, FirstName, LastName, 0 as Deleted, 2
                 from persons
                )
               ) pc
         ) pc
    where seqnum = 1;
    

    我不确定您如何跨表识别同一个人。这使用了名称,因为这就是您用文字描述问题的方式。

    思路如下:

    • 使用UNION ALL(不是UNION)组合所有表中的行。
    • 添加优先级列。
    • 使用ROW_NUMBER() 根据优先级选择第一个值(PARTITION BY 会发生变化,例如,如果您使用要在表之间匹配的 id)。
    • 最后选择行号为1的行。

    【讨论】:

      【解决方案3】:

      看起来你不想要一个联盟 - 而你想要一个加入。 FULL OUTER JOIN 返回两个表的所有行,并允许您通过加入您的 ID 和 LinkedID 来识别同一个人的行。

      然后,您可以使用ISNULL 函数在存在行时从联系人中进行选择,如果不存在则从人员中进行选择。

      Select ISNULL(C.ID,P.PersonGuid) as Id,
             ISNULL(C.LinkedID,Personid) as PersonId,
             ISNULL(C.Firstname,P.FirstName) as FirstName,
             ISNULL(C.Lastname,P.Lastname) as LastName,
             ISNULL(C.Deleted,cast(0 as bit)) as deleted
      From DB1.People P
      FULL OUTER JOIN DB2.Contacts C on P.PersonID = C.LinkedId
      

      【讨论】:

      • 我的示例可能过于简化了。真正的查询来自大约 6 个表的连接。您是否会改为将我的每个查询插入到临时表中并加入它们?
      • 要么将您的联接替换为FROM (big join for people here ) P FULL OUTER JOIN (big join for Contacts here) C ON .....,要么构建联接表的视图,然后引用它。
      【解决方案4】:

      UNION 将为您提供没有重复的记录,因此要使用联系人覆盖人员数据,请更改查询的顺序:

      Select Id, LinkedId as PersonId, FirstName, LastName, Deleted 
      from DB2.Contacts
      
      UNION
      
      Select PersonGuid as Id,Personid,Firstname,LastName,cast(0 as bit) as deleted 
      From DB1.People
      

      要获取所有记录,请使用UNION ALL 而不是UNION。这也将返回重复项。

      【讨论】:

        【解决方案5】:

        尝试以下查询。

        Select PersonGuid as Id,
        Personid,
        Firstname,
        LastName,
        cast(0 as bit) as deleted
        From DB1.People
        where not exists (select 1 from Contacts WHERE personID=LinkedId and deleted=1)
        
        Union
        
        Select
        Id,
        LinkedId as PersonId,
        FirstName,
        LastName,
        Deleted 
        from DB2.Contacts
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-15
        • 2011-01-21
        • 2018-11-01
        • 1970-01-01
        相关资源
        最近更新 更多