分类: Mysql/postgreSQL
2008-09-05 21:27:44
2.4 表达式求值和类型转换
MySQL允许编写包括常量、函数调用和表列引用的表达式。这些值可利用不同类型的运算符进行组合,诸如算术运算符或比较运算符。表达式的项可用圆括号来分组。表达式在SELECT 语句的列选择列表和WHERE 子句中出现得最为频繁,如下所示:
所选择的每列给出了一个表达式,如WHERE 子句中所示的那样。表达式也出现在DELETE 和UPDATE语句的WHERE 子句中,以及出现在INSERT 语句的VALUES( ) 子句中。
在MySQL遇到一个表达式时,它对其求值得出结果。例如, (4 * 3)/(4 - 2) 求值得6。表达式求值可能涉及类型转换。例如, MySQL在数960821用于需要日期值的环境时,将其转换为日期“ 19 9 6 - 0 8 - 2 1”。本节讨论怎样编写MySQL的表达式,以及在表达式求值中MySQL所使用的类型转换规则。每个MySQL的运算符都介绍过了,但MySQL有那么 多的函数,我们只接触过几个。每个运算符和函数的进一步介绍可参阅附录C。
2.4.1撰写表达式
表达式可以只是一个简单的常量,如:
0 数值常量
“a b c” 串常量
表达式可以进行函数调用。有的函数需要参数(圆括号中有值),而有的不需要。多个参数应该用逗号分隔。在调用一个函数时,参数旁边可以有空格,但在函数名与圆括号间不能有空格。下面是一些函数例子:
NOW( ) 无参数函数
STRCMP (“a b c”, “d e f”) 有两个参数的函数
STRCMP( “a b c”, “d e f”) 参数旁边有空格是合法的
STRCMP (“a b c”, “d e f”) 函数名后跟空格是不合法的
如果函数名后有一个空格, MySQL的分析程序可能会将函数名解释为一个列名(函数名不是保留字,如果需要的话,可将它们用作列名)。其结果是出现一个语法错误。表达式中可使用表 列。最简单的情形是,当某个列所属的表在上下文中是明确的,则可简单地给出列名对该列进行引用。下面的每个SELECT 语句中惟一地出了一个表名,因此,列的引用无歧义:
总之,可以组合所有这些值以得到更为复杂的表达式。
1. 运算符的类型
MySQL有几种类型的运算符,可 用来连接表达式的项。算术运算符,如表2-15 所示,一般包括加、减、乘、除以及模运算符。在两个操作数都是整数时,“+”、“-”和“ *”算术运算用BIGINT(64 位)整数值来完成。而在结果预期为一个整数时,“/”和“%”也是用BIGINT(64 位)整数值来完成的。应该认识到,如果某个运算涉及更大的值,如结果超过64 位,其结果不可预料。
模式匹配允许查找值而不必给出精确的直接值。MySQL利用LIKE 运算符和通配符“%”(匹配任意的字符序列)和“ _”(匹配任意单个字符),提供SQL 的模式匹配。MySQL还基于类似于诸如g r e p、sed 和vi 等UNIX 程序中所用的REGEXP 运算符和扩展正规表达式,提供模式匹配。为了完成模式匹配,必须使用这些模式匹配运算符中的某一个;不能使用“ =”。为了进行相反的模式匹配,可使用NOT LIKE 或NOT REGEXP。
除了使用的模式运算符和模式字符不同外,这两种模式匹配还在以下重要的方面存在差异:
■ 除非至少有一个操作数为二进制串,否则LIKE 是不区分大小写的。REGEXP 是区分大小写的。(在MySQL3.23.4 以后的版本中,除非至少有一个操作数是二进制串,否则REGEXP 是不区分大小写的。)
■ 仅当整个串匹配,SQL 才是模式匹配的。仅当相应的模式在串中某一处出现,正规表达式才匹配。用于LIKE 运算符的模式可以包括“%”和“_”通配符。例如,模式“ F r a n k %”与任何以“F r a n k”起头的串匹配:
这不同于ANSI SQL 的LIKE 运算符,它是区分大小写的。通配符可在模式中任何地方给出。“% bert”与“ Englebert”、“Bert”和“Albert”匹配。“% bert %”也与所有这些串匹配,而且还与如像“ Berthold”、“Bertram”、和“Alberta”这样
的串匹配。LIKE 所允许的另一个通配符是“ _”,它与单个字符匹配。“_ _”与三个字符的串匹配。“c _ t”与“c a t”、“c ut”甚至“c _ t”匹配(因为“_”与自身匹配)。为了关掉“%”或“ _”的特殊含义,与这些字符的直接实例相匹配,需要在它们前面放置一个斜杠(“ %”或“ _”),如:
“任意数目”包括0个实例,这也就是为什么第二个表达式匹配成功的原因。‘^pat’和‘pat$’固定了一种模式匹配,从而模式pat 只在它出现在串的前头时匹配,而‘ ^pat$’只在pat 匹配整个串时匹配,例如:
REGEXP 模式可从某个表列中取出,虽然如果该列包含几个不同的值时,这样做比常量模式慢。每当列值更改时,必须对模式进行检查并转换成内部形式。MySQL的正规表达式匹配还有一些特殊的模式字符。要了解更详细信息请参阅附录C。
2. 运算符的优先级
当求一个表达式的值时,首先查看运算符以决定运算的先后次序。有的运算符具有较高的优先级;例如,乘和除比加和减的优先级更高。下面的两个表达式是等价的,因为“ *”和“/”先于“+”和“-”计算:
下面列出了运算符的优先级,从高到低。列在同一行中的运算符具有相同的优先级。优先级较高的运算符在优先级较低的运算符之前求值。可用圆括号来忽略运算符的优先级并改变表达式的求值顺序,如:
3. 表达式中的NULL 值
如果将NULL 用于逻辑运算符,NULL 被认为是假:
如果给函数一个NULL 参数,除了那些处理NULL 参数的函数外,一般返回一个NULL结果。例如,IFNULL( ) 能够处理NULL 参数并适当地返回真或假。STRCMP( ) 期望一个非NULL 的参数;如果它发现传给它的是一个NULL 参数,则返回NULL 而不是真或假。在排序操作中,NULL 值被归到一起。在升序排序中, NULL 将排在所有非NULL 值之前(包括空串),而在降序排序中,NULL 将排在所有非NULL 值之后。
2.4.2 类型转换
MySQL根据所执行的操作类型,自动地进行大量的类型转换,任何时候,只要将一个类型的值用于需要另一类型值的场合,就会进行这种转换。下面是需要进行类型转换的原因:
■ 操作数转换为适合于某种运算符求值的类型。
■ 函数参数转换为函数所需的类型。
■ 转换某个值以便赋给一个具有不同类型的表列。下列表达式涉及类型转换。它由加运算符“ +”和两个操作数1和“2” 组成:
1+"2"
其中操作数的类型不同,一个是数,另一个是串,因此, MySQL对其中之一进行转换以便使它们两个具有相同的类型。但是应该转换哪一个呢?因为,“+”是一个数值运算符,所以MySQL希望操作数为数,因 此,将串“ 2”转换为数2。然后求此表达式的值得出3。再举一例。C O N C AT( ) 函数连接串产生一个更长的串作为结果。为了完成此工作,它将参数解释为串,而不管参数实际是何类型。如果传递给C O N C AT( ) 几个数,则它将把它们转换成串,然后返回这些串的连接,如:
CONCAT(1, 2, 3) 产生串“12 3”。表达式“12 3”/10 转换为12 3 / 10,因为除是一个算术运算符。这个表达式的结果的浮点形式为12 . 3,但REPEAT( ) 需要整数的重复计数值,所以进行整除得12。然后,REPEAT (‘X’, 12) 产生一个含有12个‘X’ 字符的结果串。一般原则是,MySQL尽量将值转换为表达式所需要的类型,尽量避免由于值的类型不对而导致错误。根据上下文, MySQL将在三种通用类型(数、串或日期与时间)之间进行值的转换。但是,值不能总是可以从一种类型转为另一种类型。如果被转换值不是给定类型的合法 值,则此转换失败。将如“a b c” 这样不像数的东西转换为数,则结果为0。将不像日期或时间的东西转换为日期或时间类型结果为该类型的“零”值。例如,将串“a b c” 转换为日期结果为“零”日期“ 0 0 0 0 - 0 0 - 0 0”。而任何值都可以处理为串,因此,一般将某个值转换为串不会产生问题。
MySQL也进行一些微小的类型转换。如果在整型环境中使用一个浮点值,此值将被转换,转换时进行四舍五入。也可以进行相反的工作;一个整数用作浮点数 也不会有问题。除非其内容显示表示一个数,否则十六进制常数一般作为串处理。在串上下文中,每对十六进制数字转换为一个字符,其结果作为串。下面是一些转 换的样例:
大小比较运算符(“<”、“< =”、“=”等等)是上下文相关的;即,它们根据操作数的类型求值。下面的表达式从数值上对操作数进行比较,因为操作符两边都是数。
2 < 11 | -> 1 |
"2" < "11" | -> 0 |
在下面的比较中,类型是混合的,因此,MySQL按数比较它们。结果是两个表达式都为真:
在下面的比较中,类型是混合的,因此,MySQL按数比较它们。结果是两个表达式都为真:在各个比较中,MySQL根据下列规则对操作数进行转换:
■ 除了“< = >”运算符外,涉及NULL 值的比较其值为NULL(除NULL <=> NULL 为真外,“< = >”与“=”相同)。
■ 如果两个操作数都是串,则按串进行字典顺序的比较。串比较利用服务器上有效的字符集进行。
■ 如果两个操作数都为整数,则按整数进行数的比较。
■ 不与数进行比较的十六进制常量按二进制串进行比较。
■ 如果其中有一个操作数为TIMES TAMP 或DATETIME 值而另一个为常量,则按TIMES TAMP 值进行比较。这样做将使比较对ODBC 应用更好。
■ 否则,两个操作数将按浮点值进行数的比较。注意,这包括一个串与一个数进行比较的情况。其中串被转换为数,如果该串转换后不是一个数,则结果为0。例如,”14 . 3”转换为14 . 3,但“L 4 . 3” 转换为0。
1. 日期与时间的解释规则
MySQL按表达式的环境将串和数自由地转换为日期和时间值,反之亦然。日期和时间值在数值上下文中转换为数;数在日期或时间上下文中转换为日期或时 间。在将一个值赋予一个日期或时间列时,或在函数需要一个日期或时间值时,进行转换为日期或时间值的转换。如果表my_table 含有一个DATE 列date _ col,下列语句是等价的:
2. 测试并强制进行类型转换
为了了解表达式中类型转换是怎样进行的,用mysql程序发布一条对表达式求值的SELECT 语句如下:
原来的打算大概是想删除包含值“0 0” 的那两行。但实际作用是删除了所有的行。之所以这样是由于MySQL的比较规则在起作用。char_col 为一个串列,但00 没有用引号括起来,因此,它被作为数对待了。按MySQL的比较规则,涉及一个串与一个数的比较按两个数的比较来求值。随着DELETE 查询的执行,char_col 的每个值被转换为0,”0 0”也被转换为0,因此,所有不类似数的串都转换成0。从而,对于每一行, WHERE 子句都为真,因此,DELETE 语句清空了该表。显然,这是一种在执行DELETE 前,应该用SELECT 语句对WHERE 子句进行测试的情况,这样将会示出表达式所选择的行太多了。如下所示:
如果不能肯定某个值的使用方式,可以利用MySQL的表达式求值机制将该值强制转换为特定的类型:
■ 增加+ 0 或+ 0.0 到某项上以强制转换到一个数值:
3. 超范围值或非法值的转换
超范围值或非法值的转换的基本原则为:无用输入,无用输出。如果不在存储日期前对其进行验证,那么可能会得到不喜欢的东西。下面给出一些MySQL处理超范围值或不合适值的一般原则,这些内容曾经在前面介绍过:
■ 对于数值或T I M E列,超出合法范围的值被剪裁为相应取值范围的最接近的数值并作为结果值存储。
■ 对于非ENUM 或SET 的串列,太长的串被截为适合该列存储的最大长度的串。ENUM 或SET 列的赋值依赖于定义列时给出的合法值。如果赋予ENUM 列一个未作为枚举成员给出的值,将会赋予一个错误成员(即,对应于零值成员的空串)。如果赋予SET 列一个包含未作为集合成员给出的子串的值,那么,那些未作为集合成员给出的子串将被删除,并将剩余成员构成的值赋给该列。
■ 对于日期或时间列,非法值被转换为该类型适当的“零”值(参阅表2 - 11)。对于非TIME 的日期和时间列,超出取值范围的值可转换为“零”值、NULL或某种其他的值(换句话说,结果是不可预料的)。
这些转换都将作为ALTER TABLE、LOAD DATA、UPDATE和多行INSERT 语句的警告信息报告。在mysql客户机中,这些信息显示在查询报告的状态行上。在编程语言中,可通过某些其他手段取得这个信息。如果使用的是 MySQLC API,那么可调用mysql_info( ) 函数来获得这个信息。对于Perl DBI API,可利用数据库连接的mysql_info 属性。所提供的这个信息是警告信息的次数计数。为了知道更改了哪些行,可发布一条SELECT ... INTOOUTFILE 查询,并将结果与原始行进行比较。