带有 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 #2
与 federal_state_id
的 2
不匹配。
如何更改 SQL 查询以获得我想要的结果?
【问题讨论】:
【参考方案1】:如果您将句点条目上的条件移动到连接条件,它们将不会成为与槽连接的集合的一部分,从而为name
和federal_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_id
或 federal_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 条件(示例)
MYSQL FULL OUTER JOIN - 使用 LEFT-UNION-LEFT JOIN 时的所有 NULL 结果
在 MYSQL 中执行 LEFT OUTER JOIN 时插入零而不是 NULL