从 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