【问题标题】:Left outer Join query returns duplicates in SQL Server左外连接查询在 SQL Server 中返回重复项
【发布时间】:2014-09-09 01:48:13
【问题描述】:

我有一个表 1 (MID, SSN, ...) MID 是主键,表 2 (ID, SSN, StateCode..) 其中 IDSSN 构成主键。我正在尝试显示表 1 中的所有列以及表 2 中的 StateCodeSSN 匹配。 Tbl 1 有 50 行,有些具有相同的 SSN 值。

如果从表 2 中没有找到 SSN 匹配,则在 StateCode 中显示 NULL 是可以接受的,所以我选择了左连接。这是我的查询

Select 
    tbl1.*, tbl2.StateCode
from 
    tbl1
left outer join 
    tbl2 on tbl1.SSN = tbl2.SSN

我正在寻找 50 条记录,但我得到了 70 条,在 tbl1 中包含相同 ssn 值的行最终会在最终输出中重复。出了什么问题?

【问题讨论】:

  • SSN 在每个表中必须是唯一的,您的联接才能返回非重复项
  • @pixelbits 如果加入不是这样做的方法,您对另一种选择有何建议?
  • 您希望在选择列表中返回什么?您的选择列表将确定查询的构造方式。它将涉及几个子选择,然后是一个连接 - 丑陋,但它会工作。
  • 我试图返回表 1 中的所有列,以及表 2 中的状态码,显示 NULL 或值。 SSN是这两个表中的公共列,表1中有重复的ssn。
  • 如果 table1 有 50 行并且与 table2 的左连接返回 70,则 table1 的某些行必须与 table2 中的 2 行或更多行匹配每个。因为它是产生重复的连接。查看连接条件,我只能得出结论,您误认为 SSN 在 table2 中是唯一的。但可以肯定的是,使用SELECT COUNT(SSN), COUNT(DISTINCT SSN) FROM table2 之类的东西应该很容易验证。

标签: sql duplicates left-join


【解决方案1】:

我建议阅读cartesian product

如果第一个表中有 50 行,第二个表中有 70 行,则为 3500 行。连接条件 tbl1.SSN = tbl2.SSN 将过滤掉行,但您最终可能会得到超过 50 行。

回到您的问题,您可以通过尝试以下操作来查看发生了什么:

SELECT 
  tbl1.*,
  (SELECT COUNT(*) FROM tbl2 WHERE tbl1.SSN = tbl2.SSN) AS NbResultTbl2
FROM 
  tbl1

这将告诉tbl1 的哪些行在tbl2 中有多个匹配项。如果您在 NbResultTbl2 列中有一个大于 1 的数字,那么您最终会得到重复。

要消除这些重复,您可以试试这个:

SELECT 
  tbl1.*,
  (SELECT TOP 1 StateCode FROM tbl2 WHERE tbl1.SSN = tbl2.SSN) 
FROM 
  tbl1  

这将获得在 tbl2 中为匹配的 SNN 找到的第一个 StateCode

【讨论】:

  • 我尝试通读,但我无法推断为什么左连接会检索到比 tbl1 中存在的行更多的行,因为表 2 中的 ssn 是不同的
  • 查看更新的答案以检查表中的ssn 是否真的不同。
  • 谢谢,这不是很明显,我想出了另一个查询,但您的查询又短又干净!很有帮助
【解决方案2】:

尝试使用 SELECT DISTINCT 而不仅仅是 SELECT 语句,SELECT DISTINCT 不会显示重复项

【讨论】:

  • 我想要表 1 中的所有列以及表 2 中的状态码,当我做不同时,我正在丢失数据,返回的行数少于 50
  • 我对你原来的问题有点困惑,你说显示空值是可以接受的。您是否尝试过添加 Where 条件,例如 WHERE ColumnName IS NOT NULL
  • 如果在表 2 中找不到表 1 中的 ssn,则可以接受 NULL 作为 StateCode 中的值。我正在尝试显示与该行的 SSN 匹配的所有 50 行以及 StateCode 跨度>
  • 另外,如果您要检索 50 条记录,您的表是否每个都有超过 50 条记录,或者一张表是否正好有 50 条记录?如果您在一张表中正好有 50 个,那么只需找出正确的连接应该很简单
  • 是的,表 1 正好有 50 行,表 2 包含不同的 SSN 和 Statecode 值。我不确定为什么我的左连接检索到的重复项和最终结果比表 1 中的要多
【解决方案3】:

试试看, 你的两个表还有一个主键所以只需尝试 ID 列来匹配

Select tbl1.MID,tbl1.SSN, tbl2.StateCode
from tbl1
left outer join tbl2 
on tbl1.MID= tbl2.ID
Group by tbl1.MID,tbl1.SSN, tbl2.StateCode

【讨论】:

    【解决方案4】:

    评论太长了。

    "ID 和 SSN 都是主键" . . .该陈述表明缺乏对主键是什么的理解。一张表只能有一个主键。主键可以是复合的(由多于一列组成),但只有一列。

    如果MID 是 table1 的主键,那么可能多行可以具有相同的 SSN。

    您的查询是:

    Select *
    from tbl1, tbl2.StateCode
    from tbl1, tbl2 left outer join tbl2 on tbl1.SSN = tbl2.SSN
    

    这甚至不是有效的 SQL。你可以试试这个版本:

    Select distinct tbl1.*, tbl2.StateCode
    from tbl1 left outer join
         tbl2
         on tbl1.SSN = tbl2.SSN;
    

    这是有效的,看起来就是你想要的。

    【讨论】:

    • 我试过这个,我仍然得到 70 行以这种方式进行左外连接,所有具有相同 ssn 的行都会重复更多。我不确定这是为什么
    • @sandr 。 . .也许select distinct 会删除您认为重复的行。
    【解决方案5】:

    尝试在 Table2 中对 SSN 进行分组并获取 MAX StateCode:

    SELECT Table1.*, DT.StateCode
    FROM Table1
    LEFT OUTER JOIN (
        SELECT SSN, MAX(StateCode) AS StateCode FROM Table2 GROUP BY SSN
    ) DT ON Table1.SSN = DT.SSN
    

    【讨论】:

    • 查询不起作用,分组是不可能的,因为其他列在这种情况下不符合要求,我正在尝试选择所有表 1 和表 2 中的一列
    • 好的,试试嵌套选择,每个 SSN 只返回一个状态码
    【解决方案6】:

    按像素位添加到评论中。您可以使用 (skip MID) 执行子查询

     select distinct ssn,* from tbl1
    

    然后将其加入到 tbl2

    除非 MID 之外的其他列是不同的,否则这应该给你 50 行

    【讨论】:

    • 当我做不同时,我实际上会丢失数据,它返回的行数更少。
    猜你喜欢
    • 2021-01-20
    • 1970-01-01
    • 2013-04-03
    • 1970-01-01
    • 2023-03-19
    • 2016-02-19
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多