三值逻辑及NULL一点延伸
flystone 2010-10-29于唐山
关键词:三值、逻辑、three-valued logic、NULL
今天我们来说说SQL的一个重要特性:三值逻辑。我们一般知道一个表达式的值可能是TRUE或FALSE,SQL引入另一个可能的值UNKNOWN。TRUE、FALSE、UNKNOWN称为三值逻辑,它是SQL的一个重要特性,正常理解是熟练操作SQL的一个条件。
在SQL的连接操作及搜索数据时使用聚合函数时,我们要注意三值逻辑的微妙之处,处理好三值逻辑还得提到一个关键点NULL值。在SQL中UNKNOWN值一般和NULL有关,也就是UNKNOWN值一般出现在包含在有NULL参与的表式中,在这儿大家一定要分清UNKNOWN作为表达式的值与NULL不是一个意思,不要混淆概念。下面概括为三个方面来说说这个话题。
一、包含NULL的表达式值(UNKNOWN)
SELECT
CASE WHEN NULL = NULL THEN 0 WHEN NULL > NULL THEN 1 WHEN NULL < NULL THEN -1 ELSE -99 END,
CASE WHEN NULL IS NULL THEN 1 ELSE 0 END,
CASE WHEN NULL = 1 THEN 1 ELSE 0 END,
CASE WHEN NULL > 1 THEN 1 ELSE 0 END,
CASE WHEN NULL IS NOT NULL THEN 1 ELSE 0 END,
CASE WHEN NULL +1 > NULL THEN 1 ELSE 0 END,
CASE WHEN NULL +1 > 1 THEN 1 ELSE 0 END
/*
--------- --------- --------- --------- --------- --------- ---------
-99 1 0 0 0 0 0
(1 行受影响)
*/
二、三值的反逻辑
not true => false
not false => true
not unknown => unknown
三、三值逻辑中的UNKNOWN在不同逻辑处理阶段的体现
在查询筛选器、约束、分组、排序中包含NULL的表达的UNKNOWN值SQL会分别对待,下面我们稍加详细说明一下,更的情况我们可以自己写SQL测试。
在ON、WHERE、HAVING查询筛选器UNKNOWN被当FALSE处理,也就是被排除在查询的结果之外的。
在CHECK约束中UNKNOWN被当作TRUE来处理的。
下面我就CHECK约束来举一个例子,在下例中我在AGE列上加了一个0-99的约束,第一条SQL插入NULL,NULL和INT值比较表达式的值应该是UNKNOWN,但是在CHECK约束中被当TRUE来对待了。
---------------------------------------------------------------------
-- Author :flystone
-- Date : 2010-11-04
-- Version: Microsoft SQL Server 2008 (SP1) - 10.0.2531.0 (Intel X86)
-- Mar 29 2009 10:27:29
-- Copyright (c) 1988-2008 Microsoft Corporation
-- Enterprise Edition on Windows NT 5.2 <X86>(Build 3790: SP2)
--
---------------------------------------------------------------------
CREATE TABLE TA(ID INT,AGE INT CHECK(AGE BETWEEN 1 AND 99))
GO
INSERT INTO TA(ID ) SELECT 1
/*
(1 行受影响)
*/
GO
INSERT INTO TA SELECT 1,-1
/*
消息547,级别16,状态0,第1 行
INSERT 语句与CHECK 约束"CK__TA__AGE__25869641"冲突。该冲突发生于数据库"CSDN2008",表"DBO.TA", COLUMN 'AGE'。
*/
GO
SELECT * FROM TA
/*
ID AGE
----------- -----------
1 NULL
*/
DROP TABLE TA
最后在UNIQUE约束、分组或排序中,包含NULL列值比较时,表达式的值被当作TRUE看待,关于这一点我们应该很好理解,比如在排序时所有NULL排在一起,分组时NULL的值汇总在一起,包含UNIQUE约束的列是不能插入两行NULL值的,这些我们都可以写SQL来检测,在这儿不一一列举。
因为NULL的参与引入的UNKNOWN值使得我们在处理表达值时必须严谨,否则引起意想不到结果。在这儿我又想在引擎中提到表结构时,这个NULL引起的纷争,就是关于列是否允许为空的问题,正反两方各有各的理由,似乎很难做出一个错或对的判断,不过我们一定可以清醒的意识到,NULL值的处理必然会增加引擎的复杂度,因为我们知道SQL通过bitmap来管理行的记录是否有空值,如果有列允许为NULL,那么在访问每一行时都必须对bitmap进行解码,显然会增加引擎的复杂度,所以严谨些个人还是赞同对所有的列加上缺省并指定为NOT NULL。