左加入怪异
Left join weirdness
至少对于 MariaDB v10.x,当有 NULL
return 值时,为什么带有左连接的真值子句不能像我预期的那样工作?
以下作品:
SELECT
u.id
FROM
user u
INNER JOIN role r on r.user = u.id
INNER JOIN customer c ON c.id = r.customer
LEFT JOIN customer_subclass cs ON cs.customer = c.id
WHERE
u.status = 'NEW' AND (cs.code != 4 OR cs.code IS NULL)
但是当我第一次尝试时
WHERE
u.status = 'NEW' AND cs.code != 4
当 cs.code
为 NULL
时它不起作用。为什么我必须专门针对 NULL
本身进行测试?我假设 NULL != 4
?
因为与 null
比较结果既没有 true
也没有 false
。 未知。
我知道的所有数据库引擎都是如此。 is
运算符特别处理您已经使用的 null
值。
在 WHERE
子句中使用 LEFT JOIN
右侧 table 的 cs.code != 4
条件,LEFT JOIN
作为常规 INNER JOIN
.
解决方案是将该条件移动到 ON
子句,以获得真正的 LEFT JOIN
行为。
SELECT
u.id
FROM
user u
INNER JOIN role r on r.user = u.id
INNER JOIN customer c ON c.id = r.customer
LEFT JOIN customer_subclass cs ON cs.customer = c.id AND cs.code != 4
WHERE
u.status = 'NEW'
问题是引擎是建立在三值谓词逻辑之上的。如果谓词比较两个非空值,那么它可以被评估为 TRUE
或 FALSE
。如果 then 中至少有一个是 NULL
,则 predicate 的计算结果为第三个逻辑值 - UNKNOWN
.
现在 WHERE
子句中发生了什么?它的设计方式是 returns 行,其中谓词的计算结果仅为 TRUE
!如果谓词的计算结果为 FALSE
或 UNKNOWN
,则相应的行会从结果集中过滤掉。
起初这非常令人困惑,并导致新手进入 SQL
的世界时犯下几个典型的错误。他们只是不认为数据可能包含 NULL
s。例如,其中一个经典错误是:
Employyes(Name varchar, Contry varchar)
'John', 'USA'
'Peter', NULL
'Mike', 'England'
并且您想要 Contry
不是 USA
的所有行。你只需写:
select * from Employees where Country <> 'USA'
并且只得到:
'Mike', 'England'
因此。乍一看这很令人困惑,但据您了解引擎正在执行三值逻辑,结果是合乎逻辑的。
至少对于 MariaDB v10.x,当有 NULL
return 值时,为什么带有左连接的真值子句不能像我预期的那样工作?
以下作品:
SELECT
u.id
FROM
user u
INNER JOIN role r on r.user = u.id
INNER JOIN customer c ON c.id = r.customer
LEFT JOIN customer_subclass cs ON cs.customer = c.id
WHERE
u.status = 'NEW' AND (cs.code != 4 OR cs.code IS NULL)
但是当我第一次尝试时
WHERE
u.status = 'NEW' AND cs.code != 4
当 cs.code
为 NULL
时它不起作用。为什么我必须专门针对 NULL
本身进行测试?我假设 NULL != 4
?
因为与 null
比较结果既没有 true
也没有 false
。 未知。
我知道的所有数据库引擎都是如此。 is
运算符特别处理您已经使用的 null
值。
在 WHERE
子句中使用 LEFT JOIN
右侧 table 的 cs.code != 4
条件,LEFT JOIN
作为常规 INNER JOIN
.
解决方案是将该条件移动到 ON
子句,以获得真正的 LEFT JOIN
行为。
SELECT
u.id
FROM
user u
INNER JOIN role r on r.user = u.id
INNER JOIN customer c ON c.id = r.customer
LEFT JOIN customer_subclass cs ON cs.customer = c.id AND cs.code != 4
WHERE
u.status = 'NEW'
问题是引擎是建立在三值谓词逻辑之上的。如果谓词比较两个非空值,那么它可以被评估为 TRUE
或 FALSE
。如果 then 中至少有一个是 NULL
,则 predicate 的计算结果为第三个逻辑值 - UNKNOWN
.
现在 WHERE
子句中发生了什么?它的设计方式是 returns 行,其中谓词的计算结果仅为 TRUE
!如果谓词的计算结果为 FALSE
或 UNKNOWN
,则相应的行会从结果集中过滤掉。
起初这非常令人困惑,并导致新手进入 SQL
的世界时犯下几个典型的错误。他们只是不认为数据可能包含 NULL
s。例如,其中一个经典错误是:
Employyes(Name varchar, Contry varchar)
'John', 'USA'
'Peter', NULL
'Mike', 'England'
并且您想要 Contry
不是 USA
的所有行。你只需写:
select * from Employees where Country <> 'USA'
并且只得到:
'Mike', 'England'
因此。乍一看这很令人困惑,但据您了解引擎正在执行三值逻辑,结果是合乎逻辑的。