跨三个表的 t-sql 外连接

Posted

技术标签:

【中文标题】跨三个表的 t-sql 外连接【英文标题】:t-sql outer join across three tables 【发布时间】:2011-05-13 18:42:54 【问题描述】:

我有三张桌子:

CREATE TABLE person
    (id int,
    name char(50))

CREATE TABLE eventtype
    (id int,
     description char(50))

CREATE TABLE event
    (person_id int,
     eventtype_id int,
     duration int)

我想要的是一个单一的查询,它为我提供了每个人的每个事件类型的总持续时间列表,包括所有零条目。例如。如果有 10 个人和 15 种不同的事件类型,则应该返回 150 行,与事件表的内容无关。

我可以让外连接在两个表之间工作(例如,所有事件类型的持续时间),但不能使用第二个外连接。

谢谢!

【问题讨论】:

【参考方案1】:

如果您希望 personeventtype 的每个组合都有一行,则建议使用 CROSS JOIN。要获得我们需要加入event 的持续时间,但这需要是OUTER 加入,因为可能并不总是有一行。您对“total”的使用表明对于personevent 的给定组合,可能有多个event,因此我们还需要一个SUM

样本数据:

insert person values ( 1, 'Joe' )
insert person values ( 2, 'Bob' )
insert person values ( 3, 'Tim' )

insert eventtype values ( 1, 'Cake' )
insert eventtype values ( 2, 'Pie' )
insert eventtype values ( 3, 'Beer' )

insert event values ( 1, 1, 10 ) 
insert event values ( 1, 2, 10 ) 
insert event values ( 1, 2, 5 ) 
insert event values ( 2, 1, 10 ) 
insert event values ( 2, 2, 7 ) 
insert event values ( 3, 2, 8 ) 
insert event values ( 3, 3, 16 ) 
insert event values ( 1, 1, 10 ) 

查询:

SELECT
    PET.person_id
    , PET.person_name
    , PET.eventtype_id
    , PET.eventtype_description
    , ISNULL(SUM(E.duration), 0) total_duration
FROM
    (
    SELECT
        P.id person_id
        , P.name person_name
        , ET.id eventtype_id
        , ET.description eventtype_description
    FROM
        person P
        CROSS JOIN eventtype ET
    ) PET
    LEFT JOIN event E ON PET.person_id = E.person_id
                     AND PET.eventtype_id = E.eventtype_id
GROUP BY
    PET.person_id
    , PET.person_name
    , PET.eventtype_id
    , PET.eventtype_description

输出:

person_id   person_name eventtype_id eventtype_description total_duration
----------- ----------- ------------ --------------------- --------------
1           Joe         1            Cake                  20
1           Joe         2            Pie                   15
1           Joe         3            Beer                  0
2           Bob         1            Cake                  10
2           Bob         2            Pie                   7
2           Bob         3            Beer                  0
3           Tim         1            Cake                  0
3           Tim         2            Pie                   8
3           Tim         3            Beer                  16
Warning: Null value is eliminated by an aggregate or other SET operation.

(9 row(s) affected)

【讨论】:

谢谢 - 这也有效,并且也让我非常清楚这个过程 - 外连接不是我想要的。【参考方案2】:

您可以交叉连接人员和事件类型,然后只需将结果连接到事件表:

SELECT
   p.Name,
   et.Description,
   COALESCE(e.duration,0)
FROM
   person p
      cross join
   eventtype et
      left join
   event e
      on
         p.id = e.person_id and
         et.id = e.eventtype_id

交叉连接是一种交叉连接,对于左表中的每一行,它都连接到右表中的每一行。

【讨论】:

【参考方案3】:

您必须在组合中添加CROSS APPLY 才能获得不存在的关系。

SELECT  q.name, q.description, SUM(q.Duration)
FROM    (
          SELECT  p.Name, et.description, Duration = 0
          FROM    person p
                  CROSS APPLY eventtype et
          UNION ALL        
          SELECT  p.Name, et.description, e.duration
          FROM    person p
                  INNER JOIN event e ON e.person_id = p.id
                  INNER JOIN eventtype et ON et.id = e.eventtypeid        
        ) q
GROUP BY
        q.Name, q.description        

【讨论】:

以上是关于跨三个表的 t-sql 外连接的主要内容,如果未能解决你的问题,请参考以下文章

csharp 解析通过T-SQL从连接子句推断外键 - 注意:不完整。只支持seroc中的sprocs和一些语法结构

左外连接和右外连接的区别

表的连接查询

7-10外连接查询

数据库操作-内连接外连接

数据库中内外连接