三值逻辑的真值表(AND)
AND t u ft t u f
u u u f
f f f f
三值逻辑的真值表(OR)
OR t u ft t t t
u t u u
f t u f
(9)避免使用 NULL
从上面的叙述,你可以看出 NULL 是有多么特殊和多么容易引起错误了。
NULL 最恐怖的地方就在于即使你认为自己已经完全驾驭它了,但还是一不小心就会被它在背后捅一刀。
1、避免使用的方法
加上 NOT NULL 约束
使用默认值
编号:使用异常编号
例如 ISO 的性别编号中,除了 “1: 男性”,“2: 女性”,还定义了 “0: 未知”,“9: 不适用” 这两个用于异
常情况的编号。
名字:使用“无名氏”
例如名字用 “未知” or “UNKNOWN” 代替,类别用"-"代替。
数值:使用 0
日期:用最大值或最小值代替
例如开始日期和结束日期,可以使用 0000-01-01 或者 9999-12-31。
2、但你无法100%避免
无法完全消除 NULL 的原因是它扎根于关系数据库的底层中。仅靠上面提到的方法并不足够。
例如,使用外连接,或者 SQL-99 中添加的带 CUBE 或 ROLLUP 的 GROUP BY 时,还是很容易引入 NULL 的。
3、结论
因此我们能做的最多也只是 “尽量”去避免 NULL 的产生,并在不得不使用时适当使用。
(10)拓展 —— EXISTS vs. IN注意:由于有 NULL 捣鬼,所以 IN 会返回 true / fasle / unknown,而 EXISTS 只会返回 true / false。因此,IN 和 EXISTS 可以互相替换使用,而 NOT IN 和NOT EXISTS 却不可以。
具体原因可以回去翻阅原书,这里不赘述。
问:那参数是子查询时,用 IN 还是 EXISTS 更好呢?
-- IN SELECT * FROM Class_A WHERE id IN ( SELECT id FROM Class_B ); -- EXISTS SELECT * FROM Class_A A WHERE EXISTS ( SELECT * FROM Class_B B WHERE A.id = B.id );如果把上例的 Class_A 看成外表,Class_B 看成内表的话。
答:
维度一:从外表和内表的数据行大小的关系来看
1、IN 只执行一次,此内表查出后就缓存了,所以 IN 适合 外表 > 内表 的情况;
2、EXISTS 是针对外表去作循环,每次循环会跟内表作关联子查询,所以 EXISTS 适合 外表 < 内表 的情况;
3、当 内外表 数据差不多大时,IN 与 EXISTS 也差不多。
维度二:索引的角度
EXISTS 可以用到索引,而 IN 不行。所以 EXISTS 更佳。
维度三: 是否全表遍历
针对内表,EXISTS 只要查到一行数据满足条件就会终止遍历,而 IN 必须遍历整个内表。 所以 EXISTS 更佳。
维度四: 可读性
IN 更佳。
综上所述:还是考虑实际情况。但是 EXISTS 替代 IN 提高性能的可能性更大。
1、要想改善 IN 的性能,除了使用 EXISTS,还可以使用连接。
2、最近有很多数据库也尝试着改善了 IN 的性能。例如,在 Oracle 数据库中,如果我们使用了建有索引的列,那么即使使用 IN 也会先扫描索引;PostgreSQL 从版本 7.4 起也改善了使用子查询作为 IN 谓词参数时的查询速度。
注意:其实这个问题也可以当成 非关联子查询 vs. 关联子查询 来看待(除了维度三是 EXISTS 特有的优势外,其他的维度都适用)。
3、CASE 表达式CASE表达式的语法分为简单CASE表达式和搜索CASE表达式两种。
由于 搜索CASE 表达式 包含了 简单CASE 表达式 的全部功能,所以有更强大的表达能力。
注意:CASE 表达式是一种表达式而不是语句,CASE 表达式经常会因为同编程语言里的 CASE 混淆而被叫作 CASE 语句,其实是不对的。(你也可以把 CASE 表达式理解成一种函数,运行后会返回值)
编程语言中的 CASE 语句,还有 break 的概念,而 SQL 中的 CASE 表达式没有。