在SQL SERVER 中逻辑表达式存在三种值:TRUE+FALSE+UNKNOWN。UNKNOW可以理解为不确定,既不是TRUE又不是FALSE的表达式,主要由与NULL相关的逻辑判断引起,值为NULL就意味着该未赋值或该值未确定。
与NULL值做算术运算时,其结果是NULL,如果1+NULL结果为NULL
SQL Server不同场景下对UNKNOWN处理不同,对NULL的处理也不同。
1. 在WHERE+ON+HAVING三种筛选器中,所有运算结果非TURE(FALSE 与UNKNOW)的记录都不会返回;
2. 在CHECK约束中,所有运算结果为非FALSE(TRUE与UNKNOW)的都属于满足CHECK约束的;
3. 在UNIQUE约束中,如果列定义未限制为NOT NULL,那么允许该列存在一条NULL值,如果另外插入或更新一条记录为NULL时,会违法UNIQUE约束,NULL与NULL是相等的;
4. 在GROUP BY中,NULL值被认为相同而分为一组,NULL与NULL是相等的;
5. 在ORDER BY中,NULL值被认为相同而排列在一起,所有NULL值比已知值小,NULL与NULL是相等的;
除上述3/4/5条中提到的情况外,NULL与NULL是不相等的。
默认情况下,即SET ANSI_NULLS ON时,对于条件(WHERE C1 = NULL)这种查询,C1列值为NULL的行不会被返回;
而当SET ANSI_NULLS ON时,对于条件(WHERE C1 = NULL)这种查询,C1列值为NULL的行会被返回,此时C1=NULL应该被理解为做C1 IS NULL 运算,而不应该理解为NULL与NULL相等;
理解混乱的同学可以做以下测试:
--========================================== --生成测试数据 DECLARE @TB TABLE ( C1 INT ) INSERT INTO @TB SELECT 1 UNION SELECT NULL --=========================================== --修改默认选项值,设置ANSI_NULLS --注意该设置是回话级别,而不是批处理级别或语句级别 SET ANSI_NULLS OFF; --============================================ --当ANSI_NULLS OFF时,C1=NULL 等同于C1 IS NULL --因此查询返回一条NULL的记录 SELECT * FROM @TB WHERE C1=NULL --============================================ --当ANSI_NULLS OFF,NULL与NULL仍不认为相等 --因此查询中不会返回NULL的记录 SELECT * FROM @TB AS TB1 INNER JOIN @TB TB2 ON TB1.C1=TB2.C1
运行结果为:
为规范操作和避免混乱,强烈建议使用IS NULL 和IS NOT NULL判断值是否为NULL,避免修改默认选项ANSI_NULLS为OFF。
除上面提到的特殊情况外,由于NULL与NULL是不相等的,因此
1. 在做IN和EXISTS运算如WHERE C1 IN(SELECT ID FROM TB1) 或者 WHERE EXISTS (SELECT ID FROM TB1 WHERE ID =C1)时,所有C1列为NULL的行都不会被返回,无论表TB1的ID列是否存在NULL值;
2. 对NOT IN运算如T1.C1 NOT IN (SELECT T2.C1 FROM T2) 时,如果表T2的C1列存在NULL值, 那么查询将不会返回任何记录,无论表T1的C1列是否有NULL值存在。
3. 对NOT EXISTS运算如NOT EXISTS(SELECT C1 FROM T2 WHERE T2.C1=T1.C1),会返回T1表有但T2表没有且T1.C2 IS NOT NULL的记录
测试DEMO
--================= --生成测试数据 DECLARE @TB TABLE ( C1 INT ) DECLARE @TB2 TABLE ( C1 INT ) INSERT INTO @TB SELECT 1 UNION SELECT 2 INSERT INTO @TB2 SELECT 1 UNION SELECT NULL --================================= --NOT EXISTS返回记录为2的行 SELECT * FROM @TB AS TB1 WHERE NOT EXISTS( SELECT C1 FROM @TB2 AS TB2 WHERE TB2.C1=TB1.C1) --================================ --NOT IN 不返回任何行 SELECT * FROM @TB WHERE C1 NOT IN ( SELECT C1 FROM @TB2 )
因此,IN和EXIST可以相互改写,但是NOT IN不能随便改写为NOT EXISTS.
PS: 对于运算结果为UNKNOW的逻辑表达式,再做NOT运算,结果仍未UNKNOW
--==========================================================
妹子振贴