使用IS NULL进行LEFT OUTER JOIN

时间:2017-09-28 13:08:05

标签: sql postgresql

我有一个LEFT OUTER JOIN链,只适用于90%的情况。

days:
id, date_value
1, 2017-01-01
2, 2017-01-02
3, 2017-01-03

periods:
id, starts_on, ends_on, name, federal_state_id, country_id
1, 2017-01-01, 2017-01-01, Test1, 1, NULL
2, 2017-01-03, 2017-01-03, Test2, 2, NULL

slots:
id, days_id, periods_id
1, 1, 1
2, 3, 2

SQL-Query

SELECT days.date_value, periods.name, periods.federal_state_id 
FROM days 
LEFT OUTER JOIN slots ON (days.id = slots.day_id) 
LEFT OUTER JOIN periods ON (slots.period_id = periods.id) 
WHERE days.date_value >= '2017-01-01' AND 
days.date_value <='2017-01-03' AND 
(periods.id IS NULL OR periods.country_id = 1 OR 
periods.federal_state_id = 1) 
ORDER BY days.date_value;

结果

2017-01-01, Test1, 1
2017-01-02, NULL, NULL

但我想得到这个结果:

2017-01-01, Test1, 1
2017-01-02, NULL, NULL
2017-01-03, NULL, NULL

根据我的理解,periods.id IS NULL与最后一个条目不匹配,因为period #2的{​​{1}}为federal_state_id

如何更改SQL-Query以获得我想要的结果?

2 个答案:

答案 0 :(得分:2)

如果将句点条目上的条件移动到连接条件,它们将不会成为与插槽连接的集合的一部分,从而为NULL和{{1}生成所需的name }}。因此,将要加入的集合仅包含:

federal_state_id

因此,可以返回日期的每个条目(范围内的条件除外)。该查询将如下所示:

periods:
id, starts_on, ends_on, name, federal_state_id, country_id
1, 2017-01-01, 2017-01-01, Test1, 1, NULL

请注意,不再需要条件SELECT days.date_value, periods.name, periods.federal_state_id FROM days LEFT OUTER JOIN slots ON (days.id = slots.day_id) LEFT OUTER JOIN periods ON (slots.period_id = periods.id) AND ((periods.country_id = 1 OR periods.federal_state_id = 1)) WHERE days.date_value >= '2017-01-01' AND days.date_value <='2017-01-03' ORDER BY days.date_value; ,因为条件应用的条件仅包含插槽和句点,其中每个句点条目都将具有id值,因为它是主键。由于插槽和句点由左连接插槽连接,因此不会删除句点表中没有匹配项。

在原始查询中,您首先加入了句点的所有条目(如果ID匹配),然后删除所有没有所需periods.id IS NULLcountry_id的条目。这删除了federal_state_id中匹配但不符合条件的日期。

答案 1 :(得分:1)

我总是建议格式化SQL语句。错误更容易找到。

将条件移动到连接,如:

         SELECT days.date_value
               ,periods.name
               ,periods.federal_state_id 
           FROM dbo.days 
LEFT OUTER JOIN dbo.slots 
             ON days.id = slots.days_id
LEFT OUTER JOIN dbo.periods 
             ON slots.periods_id = periods.id
            AND (
                 periods.country_id          = 1 
                 OR periods.federal_state_id = 1
                )  
          WHERE days.date_value  >= '2017-01-01' 
            AND days.date_value  <='2017-01-03'  
       ORDER BY days.date_value
       ;