从 LEFT OUTER JOIN 中删除重复项

Posted

技术标签:

【中文标题】从 LEFT OUTER JOIN 中删除重复项【英文标题】:Remove Duplicates from LEFT OUTER JOIN 【发布时间】:2011-02-01 18:18:09 【问题描述】:

我的问题与Restricting a LEFT JOIN 非常相似,但有所不同。

假设我有一张桌子 SHOP 和另一张桌子 LOCATION。 Location 是表 SHOP 的一种子表,它有两列感兴趣,一个是 Division Key(简称为 KEY)和一个“SHOP”编号。这与表 SHOP 中的数字“NO”匹配。

我试过这个左外连接:

SELECT S.NO, L.KEY
FROM SHOP S
LEFT OUTER JOIN LOCATN L ON S.NO = L.SHOP

但我得到了很多重复,因为有许多位置属于一个商店。我想消除它们,只得到一个“商店,钥匙”条目的列表,没有重复。

数据正确但重复出现如下:

SHOP     KEY
 1       XXX
 1       XXX
 2       YYY
 3       ZZZ
 3       ZZZ  etc.

我希望数据显示如下:

SHOP     KEY
 1       XXX
 2       YYY
 3       ZZZ  etc.

SHOP表:

 NO
 1       
 2       
 3       

LOCATION 表:

 LOCATION   SHOP  KEY
   L-1       1    XXX   
   L-2       1    XXX   
   L-3       2    YYY   
   L-4       3    YYY   
   L-5       3    YYY   

(ORACLE 10g 数据库)

【问题讨论】:

你不应该得到任何重复,但是,正如你所说,如果你有不止一个商店的位置记录,你可能会得到不止一个商店的钥匙。请解释或举例说明“重复”的含义。 @Marcus 我一开始也是这么想的,但我假设多个位置可以有相同的除法键。 @Marcus & Martin:啊,我想我没有说清楚。是的,多个位置可以并且确实具有相同的分区键。 (严格来说 divnkey 是 shop 的父级。所以层次结构类似于 Divnkey > Shop > location)。我正在尝试用适当的部门键数据填充 Shop 的表。可能听起来很奇怪,但这是一个一次性的过程,我试图从表 LOCATION 的数据中为表 SHOP 生成更新脚本 - 通过选择“更新商店设置 divnkey = ....”命令。不想让问题复杂化,所以做一个简单的选择。 抱歉,刚刚看到您的回复。将再次更新我的答案。 【参考方案1】:

您需要 GROUP BY 'S.No' & 'L.KEY'

SELECT S.NO, L.KEY 
FROM SHOP S 
LEFT OUTER JOIN LOCATN L 
ON S.NO = L.SHOP
GROUP BY S.NO, L.KEY

【讨论】:

+1 用于使用GROUP BY,这相当于Oracle asktom.oracle.com/pls/asktom/… 上的DISTINCT,但有时在其他引擎上可能更快,例如Postgres(使用哈希而不是排序。) 好吧,你让我觉得真的很愚蠢。这完美地工作。谢谢。但是,当左外部应该处理这个问题时,为什么根本需要 group by 的任何额外的 cmets。就像我试图了解左外层的真正工作原理一样。 @Kaushik:左外连接不等于也不暗示 GROUP BY 或 DISTINCT。 LEFT JOIN 从左(第一个)表中获取所有行,并在满足连接条件的右(第二个)表中连接所有行。在左 outer 连接中,如果在右表中找不到与左表数据匹配的数据,则仍返回左表数据,并为所有右表数据输入 NULL。没有明示或暗示的分组。我个人会使用 DISTINCT,因为我认为它更清楚地指定了意图,但 YMMV。 @Bob 感谢您的澄清。我误解了 LEFT OUTER JOIN 的含义。我认为它与普通的 LEFT JOIN 不同。现在我明白它们是一样的吗?对于互联网:Jeff A 关于理解整个连接概念的有用链接:codinghorror.com/blog/2007/10/… @Kaushik - 是的,LEFT JOIN 和 LEFT OUTER JOIN 是等价的,因为 OUTER 关键字是可选的。如果您指定 FULL、LEFT 或 RIGHT 作为连接类型,您可以跳过指定 OUTER,但我通常将其放入,因为我认为它有助于使代码更清晰。 (我知道 - 我没有将 OUTER 放入我之前的评论中 - 但是,嗯,我试图将所有内容都放入 600 个字符的限制中!是的,这就是票..!:-)【参考方案2】:

编辑在您的方案更新之后

我认为您应该能够通过一个简单的子查询来做到这一点(尽管我还没有针对 Oracle 数据库进行过测试)。类似于以下内容

UPDATE shop s
SET divnkey = (SELECT DISTINCT L.KEY FROM LOCATN L WHERE S.NO = L.SHOP)

如果商店与多个部门中的位置相关联,上述内容将引发错误。

如果您只是想忽略这种可能性并在这种情况下选择任意一种,您可以使用

UPDATE shop s
SET divnkey = (SELECT MAX(L.KEY) FROM LOCATN L WHERE S.NO = L.SHOP)

【讨论】:

很好 :) 这就是为什么获得多个角度很重要。谢谢马丁,您的解决方案绝对满足我的基本要求(但另一个答案专门负责删除重复项,因此必须标记那个)。干杯【参考方案3】:

我也遇到了这个问题,但是我无法使用 GROUP BY 来解决它,因为我还返回了 TEXT 类型的字段。 (使用 DISTINCT 也是如此)。

这段代码给了我重复:

select mx.*, case isnull(ty.ty_id,0) when 0 then 'N' else 'Y' end as inuse 
from master_x mx 
left outer join thing_y ty on mx.rpt_id = ty.rpt_id

我通过重写来修复它:

select mx.*, 
case when exists (select 1 from thing_y ty where mx.rpt_id = ty.rpt_id) then 'Y' else 'N' end as inuse
from master_x mx 

如您所见,我并不关心第二个表 (thing_y) 中的数据,只关心其中的 rpt_id 是否有大于零的匹配项。 (仅供参考:rpt_id 也不是第一张表的主键,master_x)。

【讨论】:

以上是关于从 LEFT OUTER JOIN 中删除重复项的主要内容,如果未能解决你的问题,请参考以下文章

熊猫:LEFT OUTER JOIN where(ON)2个匹配的条件[重复]

如何在没有重复的情况下在 hive 中执行 LEFT OUTER JOIN 以仅检查右表中的一个值?

SQL Server 中的 LEFT JOIN 与 LEFT OUTER JOIN

从 LEFT JOIN 查询中删除重复项

MySQL 数据库中 left outer join 和 left join 啥区别

关于mysql中的left join和left outer join的区别