知识卡片
SQL弱类型化与型转导致的诡异"并"运算
内容
SQL只支持较弱形式的强类型化:布尔值只能赋给布尔变量、数字只能赋给数字变量、
字符串只能赋给字符串变量——同大类内部的检查是有的,但同一大类内部不同的具体
类型(比如INTEGER和FLOAT)之间的比较却被允许,代价是引入隐式类型转换(型转,
coercion)。计算领域公认的原则是尽量避免型转,因为它容易出错,书中给出一个
具体反例说明这种”容许”会带来多离谱的后果:两张表T1、T2的X、Y两列分别用INTEGER
和NUMERIC(5,1)交叉定义,对它们做UNION时,SQL会把INTEGER值隐式型转成
NUMERIC(5,1),导致运算结果里出现了T1和T2任何一张原表里都从未真实存在过的行——
这明显违背了”并”这个词在直觉和关系代数意义上应有的语义(结果只应包含来自
原始输入的行)。这个案例的价值不在于”SQL有个冷门bug”,而在于揭示了一条更一般
的原则:一旦允许了看似无害的隐式类型转换,就可能在完全出乎意料的地方(不止是
比较运算,任何依赖相等判断的运算——集合并/交/差、分组、去重都算在内)产生
超出原始数据范围的结果。因此实践建议是尽量避免依赖SQL的隐式型转,确保参与
比较、并、交等运算的同名列始终使用完全相同的类型;确实需要转换时,用显式的
CAST表达出来,而不是依赖隐式规则。
参考来源
- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第2章"类型和域"2.7节
"SQL中的类型检查和型转"(源文件:OEBPS/text00025.html)
- 结论依据:原文明确"即使两个数的类型不同,它们之间的比较也是合法的……这就涉及
类型型转问题……允许型转的一个怪异后果就是某些集合并、交、差运算会产生一些在
任何运算元中都没有出现过的行……结果是由未在T1和T2表中出现的行组成的——非常
奇怪的并""只要可能就尽量避免型转……当类型转换无法避免时,建议使用CAST或CAST
的等价物进行显式类型转换"。
- 原始内容:允许型转的一个怪异后果就是某些集合并、交、差运算会产生一些在任何
运算元中都没有出现过的行……只要可能就尽量避免型转……当类型转换无法避免时,
建议使用CAST或CAST的等价物进行显式类型转换。