带有 IS NULL 的 LEFT OUTER JOIN

Posted

技术标签:

【中文标题】带有 IS NULL 的 LEFT OUTER JOIN【英文标题】:LEFT OUTER JOIN with IS NULL 【发布时间】:2017-09-28 13:08:05 【问题描述】:

我有一个 at 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 查询

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 #2federal_state_id2 不匹配。

如何更改 SQL 查询以获得我想要的结果?

【问题讨论】:

【参考方案1】:

如果您将句点条目上的条件移动到连接条件,它们将不会成为与槽连接的集合的一部分,从而为namefederal_state_id 生成所需的NULL。所以将要加入的集合将只包含:

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;

请注意,不再需要条件periods.id IS NULL,因为应用条件的集合仅包含槽和周期,其中每个周期条目都将具有 id 值,因为它是主键。并且由于槽和周期由左连接槽连接,因此周期表中没有匹配项不会被删除。

在您的原始查询中,您首先加入了 period 的所有条目(如果 id 匹配),然后删除了所有不具有所需 country_idfederal_state_id 的条目。这将删除在 period 中匹配但不满足条件的日期。

【讨论】:

【参考方案2】:

我总是推荐格式化 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
       ;

【讨论】:

以上是关于带有 IS NULL 的 LEFT OUTER JOIN的主要内容,如果未能解决你的问题,请参考以下文章

Oracle left outer join with is null in JOIN vs WHERE 条件(示例)

带有子查询语法的 LEFT OUTER JOIN

MYSQL FULL OUTER JOIN - 使用 LEFT-UNION-LEFT JOIN 时的所有 NULL 结果

在 MYSQL 中执行 LEFT OUTER JOIN 时插入零而不是 NULL

Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN

Hibernate/JPA:如何强制隐式连接使用 LEFT OUTER JOINS