知识卡片

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的等价物进行显式类型转换。