SQL如何正确进行外连接

Posted

技术标签:

【中文标题】SQL如何正确进行外连接【英文标题】:SQL how to do an outer join properly 【发布时间】:2017-11-16 14:06:04 【问题描述】:

我有一张表格,里面有一家公司的电话号码。 我的问题是,员工的每个号码我们都有一行,所以如果他有一个主电话和一个手机,我们就有两行他。

现在我需要显示所有员工的列表,包括他们的电话号码和手机号码(如果有的话)和电子邮件,但电子邮件在另一个表中。 我正在使用 SQL。

一个例子:

电话表

   ID  | EmpID  |  FullName     |  Number        |  Type     |
----------------------------------------------------------------
   115 |    02  |  ManuelSan    | +34935556663   |   Fix     |
   116 |    02  |  ManuelSan    | +34652315453   |   Mobile  |
   117 |    06  |  Camillete    | +34934445621   |   Fix     |
   118 |    07  |  MarcusEsq    | +34932547841   |   Fix     |
   119 |    08  |  FionaYem     | +34965214785   |   Fix     |
   120 |    08  |  FionaYem     | +34652132124   |   Mobile  |

电子邮件表

    ID   |   empID   |   Fullname   |        Email           |
-----------------------------------------------------------------
   25    |    02     |  ManuelSan   |  Manuelsan@gg.com      |
   26    |    06     |  Camillete   |  Camillete@gg.com      |
   27    |    07     |  MarcusEsq   |  MarcusEsq@gg.com      |
   28    |    08     |  FionaYem    |  FionaYem@gg.com       |

所以我想要这个输出

   Fullname |    Fix       |     Mobile    |      Email  
------------------------------------------------------------------
 ManuelSan  | +34935556663 | +34652315453  |  Manuelsan@gg.com
 Camillete  | +34934445621 |      NULL     |  Camillete@gg.com
 MarcusEsq  | +34932547841 |      NULL     |  MarcusEsq@gg.com
 FionaYem   | +34965214785 | +34652132124  |  FionaYem@gg.com

但我正在这样做:

SELECT distinct telf.Fullname, telf.Number, acti.EMAIL
  FROM PhoneTable telf
  left outer join EmailTable as acti on acti.empID = telf.empID

我知道我需要做其他事情,但我不知道是什么。如果他有电话和手机,我会为每位员工分两排。

我该怎么做?

问候,

【问题讨论】:

为什么所有电子邮件条目都具有相同的 ID?为什么要将员工姓名存储在两个表中?你没有员工表吗?一个员工可以有多个电子邮件吗?每个员工可以有多个修复号码吗?每个员工可以有多个手机号码吗?如果我发现同一个员工 ID 有不同的员工姓名怎么办?表的唯一键是什么? 每个员工都至少有一部手机吗?每位员工是否至少有一个电子邮件地址? 抱歉emailTable上的ID相同,刚刚改了。不,电话可以为空,但电子邮件地址是强制性的。 这并不能回答所有问题。那么电子邮件表实际上是员工表吗?每个员工只有一封电子邮件?每个员工可以有一个或零个固定电话号码和一个或零个手机号码? 【参考方案1】:

大量冗余数据,可能不一致;但是,如果我们假设 empid 定义了名称并且 PhoneTable 包含每个员工的条目,则查询可能如下所示。查询的第一部分将一个员工的不同记录组合成一个记录;然后左外连接得到相应的电子邮件。请注意,如果有员工有电子邮件但没有一个电话号码,则查询仍然不完整:

select *
from (select empid,
          max(fullname),
          max(case when type='Fix' then Number else NULL end) as fix,
          max(case when type='Mobile' then Number else NULL end) as Mobile
      from PhoneTable
      group by empid) phone
  left outer join EMailTable e on phone.empid=e.empid

【讨论】:

是的,很抱歉,我刚刚修改了它。它有效,谢谢!【参考方案2】:

试试这个:

select
e.FullName,
t1.Number as "Fix",
t2.Number as "Mobile",
em.Email
from
(
    select distinct
    EmpId,
    FullName
    from PhoneTable
) e
left join PhoneTable t1 on t1.EmpId = e.EmpId and t1.Type = 'Fix'
left join PhoneTable t2 on t2.EmpId = e.EmpId and t2.Type = 'Mobile'
left join EmailTable em on em.empID = e.EmpId

【讨论】:

【参考方案3】:

我的方法是:

SELECT mail.FullName, phonefix.Number, phonemob.Number, mail.Email
  FROM EmailTable mail
FULL OUTER JOIN PhoneTable phonefix
 ON mail.empID = phonefix.empID
AND phonefix.Type = 'Fix'
FULL OUTER JOIN PhoneTable phonemob
 ON mail.empID = phonemob
AND phonemob.Type = 'Mobile'

当然,如果您有一个 Employee 表。从员工表开始,使用左外连接链接到电话号码和邮件

【讨论】:

【参考方案4】:

如果您有固定数量的电话类型,则可以对每种类型使用一个联接来执行此操作,如下所示(假设您有一个员工表):

select e.EmpId, pt1.Number as "PtFix", pt2.Number as "PtMobile"
from Employee e
  left join PhoneTable pt1 
    on (e.EmpId = pt1.EmpId and pt1.Type = 'Fix')
  left join PhoneTable pt2 
    on (e.EmpId = PhoneTable.EmpId and pt2.Type = 'Mobile')

如果您有机会引入更多电话类型(例如 Office),您可以将它们聚合成一个数组或在 mysql 中连接:

select e.EmpId, group_concat(pt.Number) as numbers
from Employee e
 left join PhoneTable pt 
   on (e.EmpId = pt.EmpId)
group by e.EmpId

【讨论】:

【参考方案5】:

您所说的电子邮件表实际上是员工表。每个员工只有一个条目。所以每个员工都有一个电子邮件地址。至于电话号码,我想每种类型和员工可以有零个或一个电话号码。 (如果还有更多,我们就挑一个。)

select e.fullname, p.fix, p.mobile, e.email
from emails
left join
(
  select 
    empid,
    max(case when type = 'Fix' then number end) as fix,
    max(case when type = 'Mobile' then number end) as mobile
  from phones
  group by empid
) p on p.empid = e.empid;

【讨论】:

以上是关于SQL如何正确进行外连接的主要内容,如果未能解决你的问题,请参考以下文章

SQL内连接与外连接的区别

如何在 C# 中使用 LINQ 应用右外连接?

SQL中的左连接与右连接,内连接有啥区别

SQL - 范围之间的左外连接

SQL中内连接和外连接的区别

PostgreSQL同表左外连接多表