【问题标题】:left join to two inner joined tables左连接到两个内连接表
【发布时间】:2012-12-13 14:09:03
【问题描述】:

我确定我可能只是健忘或过于复杂......但这是我的场景:

日历表(记录每一年的每一天)

iso_date
01/01/01
02/01/01
03/01/01
04/01/01

场地表(每个场地的记录)

venue
All London
London Tower
London Bridge
Millenium Bridge

综合场地表(参考链接场地)

master_venue,     child_venue
All London,       All London
All London,       London Tower
All London,       London Bridge
All London,       Millenium Bridge
London Tower,     London Tower
London Bridge,    London Bridge
Millenium Bridge, Millenium Bridge

预订表(记录每次预订,包括日期和地点)

iso_date, venue,            event
01/01/01, All London,       1
02/01/01, London Tower,     2
02/01/01, Millenium Bridge, 3
04/01/01, London Bridge,    4

事件表

event, status
1,     1
2,     0
3,     1
4,     1

现在我想加入表格,这样我就可以得到每天每个场地的记录,无论它是否已被预订。如果场地有预订,我只想在活动状态为 1 时查看它。

输出

iso_date, venue,            booked
01/01/01, All London,       1
01/01/01, London Tower,     1
01/01/01, London Bridge,    1
01/01/01, Millenium Bridge, 1
02/01/01, All London,       0
02/01/01, London Tower,     0
02/01/01, London Bridge,    0
02/01/01, Millenium Bridge, 1
03/01/01, All London,       0
03/01/01, London Tower,     0
03/01/01, London Bridge,    0
03/01/01, Millenium Bridge, 0
04/01/01, All London,       0
04/01/01, London Tower,     0
04/01/01, London Bridge,    1
04/01/01, Millenium Bridge, 0

我不能在 where 子句中使用事件状态,因为它会完全删除记录。

我知道我可以使用子查询或一些复杂的 case 语句,但是我是否可以智能地加入表来解决我的问题?

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    你应该会写:

    SELECT Calendar.iso_date AS iso_date,
           Venues.venue AS venue,
           COALESCE(Events.status, 0) AS booked
      FROM Calendar
     CROSS
      JOIN Venues
      LEFT
     OUTER
      JOIN (      Bookings
             JOIN Events
               ON Events.event = Bookings.event
              AND Events.status = 1
           )
        ON Bookings.iso_date = Calendar.iso_date
       AND Bookings.venue = Venues.venue
    ;
    

    (免责声明:未经测试。)

    【讨论】:

    • 这基本上就是我目前正在做的事情。当你只有一列“预订”时非常简单,但我的实际查询有更多 - 所以我希望避免不得不“调整”每一列
    • @LeeTickett:抱歉,我不太清楚你的意思。 “条件”列是什么意思?您还期望拥有哪些其他类型的列?
    • 好吧,我在这里简化了我的问题。如果您想象我有更多的表/列要添加...我将不得不为每一列添加一个 CASE 条件以确保它仅在事件状态 = 1 时显示
    • @LeeTickett:老实说,我不确定确定我了解您需要什么(“更多表/列”非常模糊),但我已经更新了我的用我最好的猜测回答。新版本将给出与旧版本相同的结果;区别在于它的内部结构。对于对应的Events.status 不是1 的任何Bookings 记录,Bookings.XXX 现在将为空。
    • 就是这样!我正在努力让语法完全正确......我求助于left join (select * from blah join moo etc)
    【解决方案2】:

    您需要从驾驶桌开始,这是日历和场地的交叉连接。然后,加入预订以获取预订信息:

    select c.iso_date, v.venue,
           (case when b.event is NULL then 0 else 1 end) as Booked
    from calendar c cross join
         venues v left outer join
         bookings b
         on b.iso_date = c.iso_date and
            b.venue = v.venue
    

    【讨论】:

    • 这基本上就是我目前正在做的事情。当你只有一列“预订”时非常简单,但我的实际查询有更多 - 所以我希望避免不得不“条件”每一列
    • @LeeTickett。 . .这回答了你的问题。你应该问另一个能更好地描述你的情况的问题。修改当前问题对回答它的人是不公平的,可能是正确的。
    • 我没有编辑我的问题,它很清楚地指出:I know I could use a subquery or some complex case statements, but is it possible i can intelligently join the tables to solve my problem?
    【解决方案3】:
    select iso_date, 
           venue,
           max(case when events.event is not null then 1 else 0 end) as booked
        from calendar
        cross join venue
        left outer join bookings on
            bookings.date  = iso_date and
            bookings.venue = venue.venue
        left outer join events on 
            events.status = 1 and 
            events.event = bookings.event
        group by iso_date, venue
    

    更新:用两个外连接重写了查询,这样即使只获取记录的一小部分,它也会很有效。

    以下是您最有效地获得单一日期和地点的方法:

    select iso_date, 
           venue,
           max(case when events.event is not null then 1 else 0 end) as booked
        from calendar
        inner join venue on
            calendar.iso_date = '01/01/01' and
            venue.venue = 'my front lawn'
        left outer join bookings on
            bookings.date  = iso_date and
            bookings.venue = venue.venue
        left outer join events on 
            events.status = 1 and 
            events.event = bookings.event
        group by iso_date, venue
    

    【讨论】:

    • 再次,这是我考虑过但想避免的可能性之一。你知道“子查询”是如何执行的吗?
    • @LeeTickett,子查询将首先执行,然后加入外部查询。从性能的角度来看,没有特别的理由可以避免这种情况。在某些特定情况下,它可能会更慢,但在这种情况下,无论您如何设计,您的查询都需要查看 所有 预订,因此不会对您造成伤害。 (好吧,除非您的实际查询与示例查询完全不同......)
    • 但是日期和地点过滤器呢?即如果我的查询仅针对一个日期,我不希望在加入之前对所有记录执行子查询?
    • @LeeTickett,是的,但你的问题只提到获得所有的日子和场地。获取一条记录是一个完全不同的问题,需要不同的设计。
    • @LeeTickett,更新了我的答案,即使在子集上也很有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多