NULL是一个极妙的颠覆常规逻辑的结构;它的使用充满矛盾,它甚至在数据库中都不是始终如一的。NULL用来发出这样一种信号:某些数据丢失或无效,并且它永远都不等于、大于或小于任何东西。不管你对它做什么,操作的结果都是NULL。你需要一个特殊的IS NULL操作符来判断NULL值,如下:
mysql> SELECT 0 > NULL, 0 = NULL, 0 < NULL, 0 IS NULL, NULL IS NULL;
+----------+----------+----------+-----------+--------------+
| 0 > NULL | 0 = NULL | 0 < NULL | 0 IS NULL | NULL IS NULL |
+----------+----------+----------+-----------+--------------+
| NULL | NULL | NULL | 0 | 1 |
+----------+----------+----------+-----------+--------------+
这就是所谓的三值逻辑。语句既不为真也不为假,而是NULL。NULL列导致很多奇怪的问题。Drupal(提醒你,并非出于有意的决定)没有太多的NULL列,从而侥幸避免了这种疯狂。唉,这不是一贯的,有时你还是被迫掉进NULL的坑里。
NULL比只是三值逻辑甚至更加颠覆常识,比如:
CREATE TABLE test1 (a int, b int);
CREATE TABLE test2 (a int, b int);
INSERT INTO test1 (a, b) VALUES (1, 0);
INSERT INTO test2 (a, b) VALUES (NULL, 1);
SELECT test1.a test1_a, test1.b test1_b, test2.a test2_a, test2.b test2_b
FROM test1
LEFT JOIN test2 ON test1.a=test2.a
WHERE test2.a IS NULL;
+---------+---------+---------+---------+
| test1_a | test1_b | test2_a | test2_b |
+---------+---------+---------+---------+
| 1 | 0 | NULL | NULL |
+---------+---------+---------+---------+
test2表中显然没有(NULL,NULL)这么一行。但是,左联接使用它来显示所谓的反联接(anti-joins)的结果。它们用来从第一个表中选择那些第二个表中没有与之匹配的行。
现在,如果1 = NULL为真,它应该找到了你插入到test2中的那单独一行,它没有那么做。记住,1 = NULL,既不是真也不是假,它就是NULL。但是仅从查询结果来看,这绝对不明显;总之,结果包含一个为NULL的test2_a,却没有执行规定的test1.a=test2.a联接不是吗?不,它确实执行了联接,但是在这个特例中,联接的部分并不需要为真。查询结果中有用NULL标记出来的丢失数据,这和联接无关。如果你发现这使人迷惑,它就是如此。你不仅需要与三值逻辑共存,还要对左联接给予豁免。
在我展示这些问题的答案之前,还有一个数据库架构细节需要涵盖。目前的SQL实现侧重于“事务(Transactions)”(该术语来源于财务交易,但它可以指数据库所执行的任何单位的工作。)你将马上会看到,这种侧重的效果与网站用户的期望是背道而驰的——因为网站用户的期望与银行用户的期望是不相同的。