左加入古怪

时间:2015-11-18 08:16:47

标签: sql left-join

至少在MariaDB v10.x中,为什么当有NULL返回值时,左连接的真实子句不能正常工作?

以下作品:

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

3 个答案:

答案 0 :(得分:1)

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

现在WHERE条款会发生什么?它的设计方式使它只返回谓词求值为TRUE的行!如果谓词的计算结果为FALSEUNKNOWN,则相应的行只会从结果集中过滤掉。

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

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

并且您希望Contry不是USA的所有行。你只需写下:

select * from Employees where Country <> 'USA'

并且只获得:

'Mike', 'England'
结果是

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

答案 1 :(得分:0)

因为与null进行比较,既不会产生true也不会产生false。它是未知

对于我所知道的所有数据库引擎都是如此。 is运算符会处理您已使用的null值。

答案 2 :(得分:0)

LEFT JOIN子句中cs.code != 4的右侧表的WHERE条件,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'