本节书摘来华章计算机《SQL与关系数据库理论——如何编写健壮的SQL代码》一书中的第2章 ,第2.8节 C. J. Date 著 单世民 何英昊 许侃 译 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.8 SQL中的字符序
SQL中涉及类型检查和型转的规则尤其是在字符串场合下的规则,要远比我到目前为止所假设的复杂,所以我要详细说说。实际上,在一本书中对这点最多只能说说皮毛,其基本的思想是:任何确定的字符串都由取自相关字符集(character set)的字符组成,并且都有一个关联的字符序(collation)。字符序是与特定字符集相关,并决定着由特定字符集的字符所组成的字符串的比较规则,也叫做核对序列(collating sequence)。设C是对应于字符集S的字符序,a和b是字符集S中的任意字符,则C必使比较表达式ab之一为真,其余为假。注意:在SQL的早期版本中只有一个字符集,其中只有一个字符序,并且此字符序所基于的还是字符集中用于表示字符的二进制码的顺序。但是,现在已经没有理由要求核对序列必须依赖于内部编码方案了,并且实际上有很好的理由要求它们不应该依赖内部编码方案。
基本思想就这么多。然而,还有些变数。其中之一源于任何确定的字符序都有PAD SPACE或NO PAD。假设字符串“AB”和“AB ”(注意第二个字符串尾部的空格)具有相同的字符集和字符序。显然,两个字符串是不同的,可是如果使用PAD SPACE则两者可认为是“比较上相等的(compare equal)”。建议:别用PAD SPACE——如果可能就一直用NO PAD。不过要注意,PAD SPACE和NO PAD的选择只会影响比较运算而已,对于赋值是没有差别的。注18
另一个变数在于:对确定的字符序,即使字符a和b不同,比较表达式a=b也可能返回TRUE。比如,定义名为CASE_INSENSITIVE的字符序,其中每个小写字母都被定义为与对应的大写字母比较上相等。因此,明显不同的字符串有时也会比较上相等。
所以,可以看到SQL中的某些v1=v2形式比较表达式即使是在v1和v2不同的情况下,也可以返回TRUE(即使它们是不同的类型也可能比较上相等,这都是因为SQL对型转的支持造成的)。我会用“相等但可区分(equal but distinguishable)”表示这样的值对(pair of values)。现在,相等比较在很多上下文(比如MATCH、LIKE、UNIQUE、UNION和JOIN)中执行(常常是隐式地),而所用的相等性实际上是“即使可区分也相等(equal even if distinguishable)”。举个例子,假设CASE_INSENSITIVE字符序如上所述定义,并将PAD SPACE用于此字符序。那么,如果表P及表SP的PNO列都使用此字符序,且表P和表SP中某行的PNO值分别是“P2”和“p2 ”,则这两行会被认为满足SP到P的外键约束,而无视外键值中的小写“p”和其尾部的空格。
而且,当计算表达式包含UNION、INTERSECT、EXCEPT、JOIN、GROUP BY、DISTINCT等运算符时,系统可能不得不从众多相同但可区分的值中选出一个作为结果中某行某列的值。不幸的是,SQL本身对此种情况没有给出完整的解决方案。结果是,在SQL没有完全说明该如何计算的情况下,一些表表达式无法得到确定的值(实际上,它们应该对不同的情况给出不同的结果)——SQL的术语是“可能非确定性的(possibly nondeterministic)”。比如,如果字符序CASE_INSENSITIVE用于表T的列C,那么即使T不发生任何变化,SELECT MAX(C) FROM T 也可能视情况返回“ZZZ”或“zzz”。
我不会对“可能非确定性的”表达式给出SQL准则(参见第12章中的深入讨论)。然而,必须提到的是,这种表达式不允许出现在完整性约束中(参见第8章),因为它们会导致不可预期的更新失败。因此要特别注意,此规则暗示了很多包含字符串类型列的表表达式(有时甚至是简单的SELECT表达式)都允许存在于约束之中!强烈建议:竭尽所能地避开“可能非确定性的”表达式。