Small. Fast. Reliable.
Choose any three.

SQLite和其他数据库引擎中的NULL处理

目的是使SQLite以符合标准的方式处理NULL。但是,SQL标准中有关如何处理NULL的描述似乎模棱两可。从标准文档中尚不清楚在所有情况下应如何正确处理NULL。

因此,无需遵循标准文档,而是对各种流行的SQL引擎进行了测试,以了解它们如何处理NULL。这个想法是使SQLite像所有其他引擎一样工作。由志愿者开发并在各种SQL RDBMS上运行SQL测试脚本,这些测试的结果用于推论每个引擎如何处理NULL值。原始测试在2002年5月运行。在该文档的末尾找到了测试脚本的副本。

SQLite最初的编码方式是,下表中所有问题的答案均​​为“是”。但是在其他SQL引擎上运行的实验表明,没有一个以这种方式工作。因此,对SQLite进行了修改,使其与Oracle,PostgreSQL和DB2相同。对于SELECT DISTINCT语句和SELECT中的UNION运算符,这涉及使NULL难以区分。NULL在UNIQUE列中仍然是不同的。这似乎有些武断,但与其他引擎兼容的愿望胜过了这一反对。

出于SELECT DISTINCT和UNION的目的,可以使SQLite将NULL区别对待。为此,应在sqliteInt.h源文件中更改NULL_ALWAYS_DISTINCT #define的值, 然后重新编译。

2003年7月13日更新: 自从本文档最初编写以来,一些经过测试的数据库引擎已经更新,并且用户已经足够友好地发送对以下图表的更正。原始数据显示了各种各样的行为,但是随着时间的流逝,行为的范围已经趋向于PostgreSQL / Oracle模型。唯一的显着区别是Informix和MS-SQL都将NULL视为UNIQUE列中的NULL。

NULL对于UNIQUE列是不同的,而对于SELECT DISTINCT和UNION却不明显,这一事实仍然令人困惑。似乎NULL应该在任何地方都没有区别,或者在任何地方都没有区别。SQL标准文档建议NULL在任何地方都应该是不同的。但是,在撰写本文时,没有经过测试的SQL引擎在SELECT DISTINCT语句或UNION中将NULL视为不同的。

下表显示了NULL处理实验的结果。

   SQLite的 PostgreSQL的 甲骨文 Informix DB2 微软SQL 豹猫
将任何内容添加到null都会得到null 是的 是的 是的 是的 是的 是的 是的
将null乘以零得到null 是的 是的 是的 是的 是的 是的 是的
null在UNIQUE列中是唯一的 是的 是的 是的 (注4) 是的
在SELECT DISTINCT中null是不同的
null在UNION中是不同的
“ CASE WHEN null THEN 1 ELSE 0 END时的情况”是0? 是的 是的 是的 是的 是的 是的 是的
“ null OR true”为true 是的 是的 是的 是的 是的 是的 是的
“不是(null和false)”为true 是的 是的 是的 是的 是的 是的 是的
   MySQL
3.23.41
MySQL
4.0.16
火鸟 SQL
Anywhere
Borland
Interbase
将任何内容添加到null都会得到null 是的 是的 是的 是的 是的
将null乘以零得到null 是的 是的 是的 是的 是的
null在UNIQUE列中是唯一的 是的 是的 是的 (注4) (注4)
在SELECT DISTINCT中null是不同的 否(注1)
null在UNION中是不同的 (注3) 否(注1)
“ CASE WHEN null THEN 1 ELSE 0 END时的情况”是0? 是的 是的 是的 是的 (注5)
“ null OR true”为true 是的 是的 是的 是的 是的
“不是(null和false)”为true 是的 是的 是的 是的
笔记:   1。  较早版本的firebird省略了SELECT DISTINCT和UNION中的所有NULL。
2。  测试数据不可用。
3。  MySQL 3.23.41版本不支持UNION。
4,  DB2,SQL Anywhere和Borland Interbase不允许在UNIQUE列中使用NULL。
5,  Borland Interbase不支持CASE表达式。

 

以下脚本用于收集上表的信息。

-我已经决定SQL对NULL的处理是反复无常的,不能
-由逻辑推论得出。必须通过实验发现它。为此,我有
-准备了以下脚本来测试各种SQL数据库如何处理NULL。
-我的目的是使用从此脚本收集的信息来使SQLite尽可能多
-尽可能与其他数据库一样。
--
-如果可以的话,请在您的数据库引擎中运行此脚本并将结果邮寄
-对于我(drh@hwaci.com),这将是很大的帮助。请务必确定
-用于此测试的数据库引擎。谢谢。
--
-如果您需要更改任何内容以使此脚本与数据库一起运行
-引擎,请发送修改后的脚本以及结果。
--

-用数据创建测试表
创建表t1(a int,b int,c int);
插入t1值(1,0,0);
插入t1值(2,0,1);
插入t1值(3,1,0);
插入t1值(4,1,1);
插入t1值(5,null,0);
插入t1值(6,null,1);
插入t1值(7,null,null);

-检查CASE在其测试表达式中使用NULL的作用
选择a,当b <> 0时为1,否则0从t1结束;
选择a + 10,如果不是b <> 0则为1,否则0从t1结束;
选择a + 20,当b <> 0和c <> 0时,则1否则0从t1结束;
选择a + 30,如果不是(b <> 0和c <> 0),则1否则0从t1结束;
选择a + 40,当b <> 0或c <> 0时,则1否则0从t1结束;
选择a + 50,如果不是(b <> 0或c <> 0),则选择1,否则0从t1结束;
选择a + 60,在b时为c的情况下,然后从1到1否则为0结束;
选择a + 70,在c时为b,然后1否则0从t1结束的情况下;

-当您将NULL乘以零时会发生什么?
从t1中选择a + 80,b * 0;
从t1中选择a + 90,b * c;

-对于其他运算符,NULL将如何处理?
从t1中选择a + 100,b + c;

-测试骨干算子的处理方式
从t1中选择count(*),count(b),sum(b),avg(b),min(b),max(b);

-检查WHERE子句中NULL的行为
从t1中选择a + 110,其中b <10;
从t1中选择a + 120,而不是b> 10;
从t1中选择a + 130,其中b <10或c = 1;
从t1中选择a + 140,其中b <10 AND c = 1;
从t1中选择a + 150,而不是(b <10 AND c = 1);
从t1选择a + 160而不是(c = 1 AND b <10);

-检查DISTINCT查询中NULL的行为
从t1中选择不同的b;

-检查UNION查询中NULL的行为
从t1联合中选择b;从t1中选择b;

-创建一个具有唯一列的新表。检查是否考虑了NULL
-截然不同。
创建表t2(a int,b int unique);
插入t2值(1,1);
插入t2值(2,null);
插入t2值(3,null);
从t2中选择*;

下降表t1;
下降表t2;