左加入怪异

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.codeNULL 时它不起作用。为什么我必须专门针对 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'

问题是引擎是建立在三值谓词逻辑之上的。如果谓词比较两个非空值,那么它可以被评估为 TRUEFALSE。如果 then 中至少有一个是 NULL,则 predicate 的计算结果为第三个逻辑值 - UNKNOWN.

现在 WHERE 子句中发生了什么?它的设计方式是 returns 行,其中谓词的计算结果仅为 TRUE!如果谓词的计算结果为 FALSEUNKNOWN,则相应的行会从结果集中过滤掉。

起初这非常令人困惑,并导致新手进入 SQL 的世界时犯下几个典型的错误。他们只是不认为数据可能包含 NULLs。例如,其中一个经典错误是:

Employyes(Name varchar, Contry varchar) 
'John', 'USA'
'Peter', NULL
'Mike', 'England'

并且您想要 Contry 不是 USA 的所有行。你只需写:

select * from Employees where Country <> 'USA'

并且只得到:

'Mike', 'England'

因此。乍一看这很令人困惑,但据您了解引擎正在执行三值逻辑,结果是合乎逻辑的。