第9章、表达式和函数
标签: MySQL是怎样使用的新版
表达式
学过小学数学的我们应该知道,将数字和运算符连接起来的组合称之为表达式
,比方说这样:
1 + 1
5 * 8
我们可以将其中的数字称之为操作数
,运算符可以称之为操作符
。特殊的,单个操作数也可以被看作是一个特殊的表达式。
在MySQL
中也有表达式的概念,不过操作数
和操作符
的含义有了扩充。下边详细看一下。
操作数
MySQL
中操作数
可以是下边这几种类型:
常数
常数很好理解,我们平时用到的数字、字符串、时间值什么的都可以被称为常数,它是一个确定的值,比如数字
1
,字符串'abc'
,时间值2019-08-16 17:10:43
啥的。列名
针对某个具体的表,它的列名可以被当作表达式的一部分,比如对于
student_info
表来说,number
、name
都可以作为操作数
。函数调用
MySQL
中有函数
的概念,比方说获取当前时间的函数NOW
,而在函数后边加个小括号就算是一个函数调用
,比如NOW()
。如果你不清楚函数的概念,我们之后会详细唠叨的,现在不知道也可以~
标量子查询或者行子查询
这个子查询我们稍后会详细唠叨的~
其他表达式
一个表达式也可以作为一个操作数与另一个操作数来形成一个更复杂的表达式,比方说(假设
col
是一个列名):(col - 5) / 3
(1 + 1) * 2 + col * 3
小贴士: 当然,可以作为操作数的东西不止这么几种,不过我们这是一个入门书籍,大家在熟练使用MySQL后再到文档中查看更多的操作数类型吧。
操作符
对于小白的我们来说,目前熟悉掌握下边三种操作符就应该够用了:
算术操作符
就是加减乘除法那一堆,我们看一下
MySQL
中都支持哪些:| 操作符 | 示例 | 描述 | | —- | —- | —- | |
+
|a + b
| 加法 | |-
|a - b
| 减法 | |*
|a * b
| 乘法 | |/
|a / b
| 除法 | |DIV
|a DIV b
| 除法,取商的整数部分 | |%
|a % b
| 取余 | |-
|-a
| 负号 |在使用
MySQL
中的算术操作符
时需要注意,DIV
和/
都表示除法操作符,但是DIV
只会取商的整数部分,/
会保留商的小数部分。比如表达式2 DIV 3
的结果是0
,而2 / 3
的结果是0.6667
。比较操作符
就是在
搜索条件
中我们已经看过的比较操作符
,我们把常用的都抄下来看一下:| 操作符 | 示例 | 描述 | | —- | —- | —- | |
=
|a = b
| a等于b | |<>
或者!=
|a <> b
| a不等于b | |<
|a < b
| a小于b | |<=
|a <= b
| a小于或等于b | |>
|a > b
| a大于b | |>=
|a >= b
| a大于或等于b | |BETWEEN
|a BETWEEN b AND c
| 满足 b <= a <= c | |NOT BETWEEN
|a NOT BETWEEN b AND c
| 不满足 b <= a <= c | |IN
|a IN (b1, b2, ...)
| a是b1, b2, … 中的某一个 | |NOT IN
|a NOT IN (b1, b2, ...)
| a不是b1, b2, … 中的任意一个 | |IS NULL
|a IS NULL
| a的值是NULL
| |IS NOT NULL
|a IS NOT NULL
| a的值不是NULL
| |LIKE
|a LIKE b
| a匹配b | |NOT LIKE
|a NOT LIKE b
| a不匹配b |由
比较操作符
连接而成的表达式也称为布尔表达式
,表示真
或者假
,也可以称为TRUE
或者FALSE
。比如1 > 3
就代表FALSE
,3 != 2
就代表TRUE
。逻辑操作符
逻辑操作符是用来将多个
布尔表达式
连接起来,我们需要了解这几个逻辑操作符
:| 操作符 | 示例 | 描述 | | —- | —- | —- | |
AND
|a AND b
| 只有a和b同时为真,表达式才为真 | |OR
|a OR b
| 只要a或b有任意一个为真,表达式就为真 | |XOR
|a XOR b
| a和b有且只有一个为真,表达式为真 |
表达式的使用
只要把这些操作数
和操作符
相互组合起来就可以组成一个表达式
。表达式
主要以下边这两种方式使用:
放在查询列表中
我们前边都是将列名放在查询列表中的(
*
号代表所有的列名~)。列名只是表达式
中超级简单的一种,我们可以将任意一个表达式作为查询列表的一部分来处理,比方说我们可以在查询student_score
表时把score
字段的数据都加100
,就像这样:mysql> SELECT number, subject, score + 100 FROM student_score;
+----------+-----------------------------+-------------+
| number | subject | score + 100 |
+----------+-----------------------------+-------------+
| 20180101 | 母猪的产后护理 | 178 |
| 20180101 | 论萨达姆的战争准备 | 188 |
| 20180102 | 母猪的产后护理 | 200 |
| 20180102 | 论萨达姆的战争准备 | 198 |
| 20180103 | 母猪的产后护理 | 159 |
| 20180103 | 论萨达姆的战争准备 | 161 |
| 20180104 | 母猪的产后护理 | 155 |
| 20180104 | 论萨达姆的战争准备 | 146 |
+----------+-----------------------------+-------------+
8 rows in set (0.00 sec)
mysql>
其中的`number`、`subject`、`score + 100`都是表达式,结果集中的列的名称也将默认使用这些表达式的名称,所以如果你觉得原名称不好,我们可以使用别名:
mysql> SELECT number, subject, score + 100 AS score FROM student_score;
+----------+-----------------------------+-------+
| number | subject | score |
+----------+-----------------------------+-------+
| 20180101 | 母猪的产后护理 | 178 |
| 20180101 | 论萨达姆的战争准备 | 188 |
| 20180102 | 母猪的产后护理 | 200 |
| 20180102 | 论萨达姆的战争准备 | 198 |
| 20180103 | 母猪的产后护理 | 159 |
| 20180103 | 论萨达姆的战争准备 | 161 |
| 20180104 | 母猪的产后护理 | 155 |
| 20180104 | 论萨达姆的战争准备 | 146 |
+----------+-----------------------------+-------+
8 rows in set (0.00 sec)
mysql>
这样`score + 100`列就可以按照别名`score`来展示了!
需要注意的是,放在查询列表的表达式也可以不涉及列名,就像这样:
mysql> SELECT 1 FROM student_info;
+---+
| 1 |
+---+
| 1 |
| 1 |
| 1 |
| 1 |
| 1 |
| 1 |
+---+
6 rows in set (0.01 sec)
mysql>
因为`student_info`中有6条记录,所以结果集中也就展示了6条结果,不过我们的查询列表处只有一个常数`1`,所以所有的结果的值也都是常数`1`。这种查询列表中不涉及列名的情况下,我们甚至可以省略掉`FROM`子句后边的表名,就像这样:
mysql> SELECT 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)
mysql>
可是这么写有什么现实用处么?好像有的,可以做个计算器\[偷笑\]~
作为搜索条件
我们在介绍搜索条件的时候介绍的都是带有列名的表达式,搜索条件也可以不带列名,比如这样:
mysql> SELECT number, name, id_number, major FROM student_info WHERE 2 > 1;
+----------+-----------+--------------------+--------------------------+
| number | name | id_number | major |
+----------+-----------+--------------------+--------------------------+
| 20180101 | 杜子腾 | 158177199901044792 | 计算机科学与工程 |
| 20180102 | 杜琦燕 | 151008199801178529 | 计算机科学与工程 |
| 20180103 | 范统 | 17156319980116959X | 软件工程 |
| 20180104 | 史珍香 | 141992199701078600 | 软件工程 |
| 20180105 | 范剑 | 181048199308156368 | 飞行器设计 |
| 20180106 | 朱逸群 | 197995199501078445 | 电子信息 |
+----------+-----------+--------------------+--------------------------+
6 rows in set (0.00 sec)
mysql>
由于我们的搜索条件是`2 > 1`,这个条件对于表中的每一条记录都成立,所以最后的查询结果就是全部的记录。不过这么写有点儿傻哈,没有一毛钱卵用,没一点实际意义~ 所以通常情况下搜索条件中都会包含列名的。
函数
我们在使用MySQL
过程中经常会有一些需求,比方说将给定文本中的小写字母转换成大写字母,把某个日期数据中的月份值提取出来等等。为了解决这些常遇到的问题,设计MySQL
的大叔贴心的为我们提供了很多所谓的函数
,比方说:
UPPER
函数是用来把给定的文本中的小写字母转换成大写字母。MONTH
函数是用来把某个日期数据中的月份值提取出来。NOW
函数用来获取当前的日期和时间。
如果我们想使用这些函数,可以在函数名后加一个小括号()
就好,表示调用一下这个函数,简称函数调用
。比方说NOW()
就代表调用NOW
函数来获取当前日期和时间。针对某些包含参数的函数,我们也可以在小括号()
里将参数填入,比方说UPPER('abc')
表示将字符串'abc'
转换为大写格式。
下边来介绍一些常用的MySQL
内置函数:
文本处理函数
名称 | 调用示例 | 示例结果 | 描述 |
---|---|---|---|
LEFT |
LEFT('abc123', 3) |
abc |
给定字符串从左边取指定长度的子串 |
RIGHT |
RIGHT('abc123', 3) |
123 |
给定字符串从右边取指定长度的子串 |
LENGTH |
LENGTH('abc') |
3 |
给定字符串的长度 |
LOWER |
LOWER('ABC') |
abc |
给定字符串的小写格式 |
UPPER |
UPPER('abc') |
ABC |
给定字符串的大写格式 |
LTRIM |
LTRIM(' abc') |
abc |
给定字符串左边空格去除后的格式 |
RTRIM |
RTRIM('abc ') |
abc |
给定字符串右边空格去除后的格式 |
SUBSTRING |
SUBSTRING('abc123', 2, 3) |
bc1 |
给定字符串从指定位置截取指定长度的子串 |
CONCAT |
CONCAT('abc', '123', 'xyz') |
abc123xyz |
将给定的各个字符串拼接成一个新字符串 |
我们以SUBSTRING
函数为例试一下:
mysql> SELECT SUBSTRING('abc123', 2, 3);
+---------------------------+
| SUBSTRING('abc123', 2, 3) |
+---------------------------+
| bc1 |
+---------------------------+
1 row in set (0.00 sec)
mysql>
我们前边在唠叨表达式
的说过,函数调用
也算是一种表达式的操作数
,它可以和其他操作数用操作符连接起来组成一个表达式来作为查询列表的一部分或者放到搜索条件中。我们来以CONCAT
函数为例来看一下:
mysql> SELECT CONCAT('学号为', number, '的学生在《', subject, '》课程的成绩是:', score) AS 成绩描述 FROM student_score;
+---------------------------------------------------------------------------------------+
| 成绩描述 |
+---------------------------------------------------------------------------------------+
| 学号为20180101的学生在《母猪的产后护理》课程的成绩是:78 |
| 学号为20180101的学生在《论萨达姆的战争准备》课程的成绩是:88 |
| 学号为20180102的学生在《母猪的产后护理》课程的成绩是:100 |
| 学号为20180102的学生在《论萨达姆的战争准备》课程的成绩是:98 |
| 学号为20180103的学生在《母猪的产后护理》课程的成绩是:59 |
| 学号为20180103的学生在《论萨达姆的战争准备》课程的成绩是:61 |
| 学号为20180104的学生在《母猪的产后护理》课程的成绩是:55 |
| 学号为20180104的学生在《论萨达姆的战争准备》课程的成绩是:46 |
+---------------------------------------------------------------------------------------+
8 rows in set (0.00 sec)
mysql>
日期和时间处理函数
下边有些函数会用到当前日期,我编辑文章的日期是2019-08-28
,在实际调用这些函数时以你的当前时间为准。
名称 | 调用示例 | 示例结果 | 描述 |
---|---|---|---|
NOW |
NOW() |
2019-08-16 17:10:43 |
返回当前日期和时间 |
CURDATE |
CURDATE() |
2019-08-16 |
返回当前日期 |
CURTIME |
CURTIME() |
17:10:43 |
返回当前时间 |
DATE |
DATE('2019-08-16 17:10:43') |
2019-08-16 |
将给定日期和时间值的日期提取出来 |
DATE_ADD |
DATE_ADD('2019-08-16 17:10:43', INTERVAL 2 DAY) |
2019-08-18 17:10:43 |
将给定的日期和时间值添加指定的时间间隔 |
DATE_SUB |
DATE_SUB('2019-08-16 17:10:43', INTERVAL 2 DAY) |
2019-08-14 17:10:43 |
将给定的日期和时间值减去指定的时间间隔 |
DATEDIFF |
DATEDIFF('2019-08-16', '2019-08-17'); |
-1 |
返回两个日期之间的天数(负数代表前一个参数代表的日期比较小) |
DATE_FORMAT |
DATE_FORMAT(NOW(),'%m-%d-%Y') |
08-16-2019 |
用给定的格式显示日期和时间 |
在使用这些函数时需要注意一些地方:
在使用
DATE_ADD
和DATE_SUB
这两个函数时需要注意,增加或减去的时间间隔单位可以自己定义,下边是MySQL
支持的一些时间单位:| 时间单位 | 描述 | | —- | —- | |
MICROSECOND
| 毫秒 | |SECOND
| 秒 | |MINUTE
| 分钟 | |HOUR
| 小时 | |DAY
| 天 | |WEEK
| 星期 | |MONTH
| 月 | |QUARTER
| 季度 | |YEAR
| 年 |如果我们相让
2019-08-16 17:10:43
这个时间值增加2分钟,可以这么写:mysql> SELECT DATE_ADD('2019-08-16 17:10:43', INTERVAL 2 MINUTE);
+----------------------------------------------------+
| DATE_ADD('2019-08-16 17:10:43', INTERVAL 2 MINUTE) |
+----------------------------------------------------+
| 2019-08-16 17:12:43 |
+----------------------------------------------------+
1 row in set (0.00 sec)
mysql>
在使用
DATE_FORMAT
函数时需要注意,我们可以通过一些所谓的格式符
来自定义日期和时间的显示格式,下边是MySQL
中常用的一些日期和时间的格式符以及它们对应的含义:| 格式符 | 描述 | | —- | —- | |
%b
| 简写的月份名称(Jan、Feb、…、Dec) | |%D
| 带有英文后缀的月份中的日期(0th、1st、2nd、…、31st)) | |%d
| 数字格式的月份中的日期(00、01、02、…、31) | |%f
| 微秒(000000-999999) | |%H
| 二十四小时制的小时 (00-23) | |%h
| 十二小时制的小时 (01-12) | |%i
| 数值格式的分钟(00-59) | |%M
| 月份名(January、February、…、December) | |%m
| 数值形式的月份(00-12) | |%p
| 上午或下午(AM代表上午、PM代表下午) | |%S
| 秒(00-59) | |%s
| 秒(00-59) | |%W
| 星期名(Sunday、Monday、…、Saturday) | |%w
| 周内第几天 (0=星期日、1=星期一、 6=星期六) | |%Y
| 4位数字形式的年(例如2019) | |%y
| 2位数字形式的年(例如19) |我们可以把我们想要的显示格式用对应的格式符描述出来,就像这样:
mysql> SELECT DATE_FORMAT(NOW(),'%b %d %Y %h:%i %p');
+----------------------------------------+
| DATE_FORMAT(NOW(),'%b %d %Y %h:%i %p') |
+----------------------------------------+
| Aug 16 2019 05:10 PM |
+----------------------------------------+
1 row in set (0.00 sec)
mysql>
`'%b %d %Y %h:%i %p'`就是一个用格式符描述的显示格式,意味着对应的日期和时间应该以下边描述的方式展示:
* 先输出简写的月份名称(格式符`%b`),也就是示例中的`Aug`,然后输出一个空格。
* 再输出用数字格式表示的的月份中的日期(格式符`%d`),也就是示例中的`16`,然后输出一个空格。
* 再输出4位数字形式的年(格式符`%Y`),也就是示例中的`2019`,然后输出一个空格。
* 再输出十二小时制的小时(格式符`%h`),也就是示例中的`05`,然后输出一个冒号`:`。
* 再输出数值格式的分钟(格式符`%i`),也就是示例中的`10`,然后输出一个空格。
* 最后输出上午或者下午(格式符`%p`),也就是示例中的`PM`。
数值处理函数
下边列举一些数学上常用到的函数,在遇到需要数学计算的业务时会很有用:
名称 | 调用示例 | 示例结果 | 描述 |
---|---|---|---|
ABS |
ABS(-1) |
1 |
取绝对值 |
Pi |
PI() |
3.141593 |
返回圆周率 |
COS |
COS(PI()) |
-1 |
返回一个角度的余弦 |
EXP |
EXP(1) |
2.718281828459045 |
返回e的指定次方 |
MOD |
MOD(5,2) |
1 |
返回除法的余数 |
RAND |
RAND() |
0.7537623539136372 |
返回一个随机数 |
SIN |
SIN(PI()/2) |
1 |
返回一个角度的正弦 |
SQRT |
SQRT(9) |
3 |
返回一个数的平方根 |
TAN |
TAN(0) |
0 |
返回一个角度的正切 |
聚集函数
如果将上边介绍的那些函数以函数调用的形式放在查询列表中,那么会为表中符合WHERE
条件的每一条记录调用一次该函数。比方说这样:
mysql> SELECT number, LEFT(name, 1) FROM student_info WHERE number < 20180106;
+----------+---------------+
| number | LEFT(name, 1) |
+----------+---------------+
| 20180101 | 杜 |
| 20180102 | 杜 |
| 20180103 | 范 |
| 20180104 | 史 |
| 20180105 | 范 |
+----------+---------------+
5 rows in set (0.00 sec)
mysql>
student_info
表中符合number < 20180106
搜索条件的每一条记录的name
字段会依次被当作LEFT
函数的参数,结果就是把这些人的名字的首个字符给提取出来了。但是有些函数是用来统计数据的,比方说统计一下表中的行数,某一列数据的最大值是什么,我们把这种函数称之为聚集函数
,下边介绍MySQL
中常用的几种聚集函数
:
函数名 | 描述 |
---|---|
COUNT |
返回某列的行数 |
MAX |
返回某列的最大值 |
MIN |
返回某列的最小值 |
SUM |
返回某列值之和 |
AVG |
返回某列的平均值 |
小贴士: 聚集函数这个名儿不太直观,把它理解为统计函数可能更符合中国人的理解习惯。
COUNT函数
COUNT
函数使用来统计行数的,它有下边两种使用方式:
COUNT(*)
:对表中行的数目进行计数,不管列的值是不是NULL
。COUNT(列名)
:对特定的列进行计数,会忽略掉该列为NULL
的行。
两者的区别是会不会忽略统计列的值为NULL的行!两者的区别是会不会忽略统计列的值为NULL的行!两者的区别是会不会忽略统计列的值为NULL的行!重要的事情说了3遍,希望你能记住。我们来数一下student_info
表中有几行记录吧:
mysql> SELECT COUNT(*) FROM student_info;
+----------+
| COUNT(*) |
+----------+
| 6 |
+----------+
1 row in set (0.00 sec)
mysql>
MAX函数
MAX
函数是用来查询某列中数据的最大值,以student_score
表中的score
列为例来看一下:
mysql> SELECT MAX(score) FROM student_score;
+------------+
| MAX(score) |
+------------+
| 100 |
+------------+
1 row in set (0.00 sec)
mysql>
score
列的最大值100
就被查找出来了~
MIN函数
MIN
函数是用来查询某列中数据的最小值,以student_score
表中的score
列为例来看一下:
mysql> SELECT MIN(score) FROM student_score;
+------------+
| MIN(score) |
+------------+
| 46 |
+------------+
1 row in set (0.00 sec)
mysql>
score
列的最小值46
就被查找出来了~
SUM函数
SUM
函数是用来计算某列数据的和,还是以student_score
表中的score
列为例来看一下:
mysql> SELECT SUM(score) FROM student_score;
+------------+
| SUM(score) |
+------------+
| 585 |
+------------+
1 row in set (0.01 sec)
mysql>
所有学生的成绩总和585
就被查询出来了,比我们用自己算快多了哈~
AVG函数
AVG
函数是用来计算某列数据的平均数,还是以student_score
表中的score
列为例来看一下:
mysql> SELECT AVG(score) FROM student_score;
+------------+
| AVG(score) |
+------------+
| 73.1250 |
+------------+
1 row in set (0.00 sec)
mysql>
可以看到平均分就是73.1250
。
给定搜索条件下聚集函数的使用
聚集函数并不是一定要统计一个表中的所有记录,我们也可以指定搜索条件来限定这些聚集函数作用的范围。比方说我们只想统计'母猪的产后护理'
这门课程的平均分可以这么写:
mysql> SELECT AVG(score) FROM student_score WHERE subject = '母猪的产后护理';
+------------+
| AVG(score) |
+------------+
| 73.0000 |
+------------+
1 row in set (0.00 sec)
mysql>
换句话说就是:不在搜索条件中的那些记录是不参与统计的。
聚集函数中DISTINCT的使用
默认情况下,上边介绍的聚集函数将计算指定列的所有非NULL
数据,如果我们指定的列中有重复数据的话,可以选择使用DISTINCT
来过滤掉这些重复数据。比方说我们想查看一下student_info
表中存储了多少个专业的学生信息,就可以这么写:
mysql> SELECT COUNT(DISTINCT major) FROM student_info;
+-----------------------+
| COUNT(DISTINCT major) |
+-----------------------+
| 4 |
+-----------------------+
1 row in set (0.01 sec)
mysql>
可以看到一共有4个专业。
组合聚集函数
这些聚集函数也可以集中在一个查询中使用,比如这样:
mysql> SELECT COUNT(*) AS 成绩记录总数, MAX(score) AS 最高成绩, MIN(score) AS 最低成绩, AVG(score) AS 平均成绩 FROM student_score;
+--------------------+--------------+--------------+--------------+
| 成绩记录总数 | 最高成绩 | 最低成绩 | 平均成绩 |
+--------------------+--------------+--------------+--------------+
| 8 | 100 | 46 | 73.1250 |
+--------------------+--------------+--------------+--------------+
1 row in set (0.00 sec)
mysql>
隐式类型转换
隐式类型转换的场景
只要某个值的类型与上下文要求的类型不符,MySQL
就会根据上下文环境中需要的类型对该值进行类型转换,由于这些类型转换都是MySQL
自动完成的,所以也可以被称为隐式类型转换
。我们列举几种常见的隐式类型转换的场景:
把操作数类型转换为适合操作符计算的相应类型。
比方说对于加法操作符
+
来说,它要求两个操作数都必须是数字才能进行计算,所以如果某个操作数不是数字的话,会将其隐式转换为数字,比方说下边这几个例子:1 + 2 → 3
'1' + 2 → 3
'1' + '2' → 3
虽然`'1'`、`'2'`都是字符串,但是如果它们作为加法操作符`+`的操作数的话,都会被强制转换为数字,所以上边几个表达式其实都会被当作`1 + 2`去处理的,这些表达式被放在查询列表时的效果如下:
mysql> SELECT 1 + 2, '1' + 2, '1' + '2';
+-------+---------+-----------+
| 1 + 2 | '1' + 2 | '1' + '2' |
+-------+---------+-----------+
| 3 | 3 | 3 |
+-------+---------+-----------+
1 row in set (0.00 sec)
mysql>
将函数参数转换为该函数期望的类型。
我们拿用于拼接字符串的
CONCAT
函数举例,这个函数以字符串类型的值作为参数,如果我们在调用这个函数的时候,传入了别的类型的值作为参数,MySQL
会自动把这些值的类型转换为字符串类型的:CONCAT('1', '2') → '12'
CONCAT('1', 2) → '12'
CONCAT(1, 2) → '12'
虽然`1`、`2`都是数字,但是如果它们作为`CONCAT`函数的参数的话,都会被强制转换为字符串,所以上边几个表达式其实都会被当作`CONCAT('1', '2)`去处理的,这些表达式被放到查询列表时的效果如下:
mysql> SELECT CONCAT('1', '2'), CONCAT('1', 2), CONCAT(1, 2);
+------------------+----------------+--------------+
| CONCAT('1', '2') | CONCAT('1', 2) | CONCAT(1, 2) |
+------------------+----------------+--------------+
| 12 | 12 | 12 |
+------------------+----------------+--------------+
1 row in set (0.00 sec)
mysql>
存储数据时,把某个值转换为某个列需要的类型。
我们先新建一个简单的表
t
:CREATE TABLE t (
i1 TINYINT,
i2 TINYINT,
s VARCHAR(100)
);
这个表有三个列,列`i1`和`i2`是用来存储整数的,列`s`是用来存储字符串的,如果我们在存储数据的时候填入的不是期望的类型,就像这样:
mysql> INSERT INTO t(i1, i2, s) VALUES('100', '100', 200);
Query OK, 1 row affected (0.01 sec)
mysql>
我们为列`i1`和`i2`填入的值是一个字符串值:`'100'`,列`s`填入的值是一个整数值:`200`,虽然说类型都不对,但是由于隐式类型转换的存在,在插入数据的时候字符串`'100'`会被转型为整数`100`,整数`200`会被转型成字符串`'200'`,所以最后插入成功,我们来看一下效果:
mysql> SELECT * FROM t;
+------+------+------+
| i1 | i2 | s |
+------+------+------+
| 100 | 100 | 200 |
+------+------+------+
1 row in set (0.00 sec)
mysql>
类型转换的注意事项
MySQL
会尽量把值转换为表达式中需要的类型,而不是产生错误。按理说
'23sfd'
这个字符串无法转换为数字,但是MySQL
规定只要字符串的开头部分包含数字,那么就把这个字符串转换为开头的数字,如果开头并没有包含数字,那么将被转换成0
,比方说这样:'23sfd' → 23
'2019-08-28' → 2019
'11:30:32' → 11
'sfd' → 0
看个例子:
mysql> SELECT '23sfd' + 0, 'sfd' + 0;
+-------------+-----------+
| '23sfd' + 0 | 'sfd' + 0 |
+-------------+-----------+
| 23 | 0 |
+-------------+-----------+
1 row in set, 2 warnings (0.00 sec)
mysql>
不过需要注意的是,这种强制转换不能用于存储数据中,比方说这样:
mysql> INSERT INTO t(i1, i2, s) VALUES('sfd', 'sfd', 'aaa');
ERROR 1366 (HY000): Incorrect integer value: 'sfd' for column 'i1' at row 1
mysql>
由于`i1`和`i2`列需要整数,而填入的字符串`'sfd'`并不能顺利的转为整数,所以报错了。
在运算时会自动提升操作数的类型。
我们知道不同数据类型能表示的数值范围是不一样的,在小的数据类型经过算数计算后得出的结果可能大于该可以表示的范围。比方说
t
表中有一条记录如下:mysql> SELECT * FROM t;
+------+------+------+
| i1 | i2 | s |
+------+------+------+
| 100 | 100 | 200 |
+------+------+------+
1 row in set (0.00 sec)
mysql>
其中的`i1`列和`i2`列的类型都是`TINYINT`,而`TINYINT`能表示的最大正整数是`127`,如果我们把`i1`列的值和`i2`列的值相加会发生什么呢?请看:
mysql> SELECT i1 + i2 FROM t;
+---------+
| i1 + i2 |
+---------+
| 200 |
+---------+
1 row in set (0.00 sec)
mysql>
可以看到最后的结果是`200`,可是它已经超过`TINYINT`类型的表示范围了。其实在运算的过程中,`MySQL`自动将整数类型的操作数提升到了`BIGINT`,这样就不会产生运算结果太大超过`TINYINT`能表示的数值范围的尴尬情况了。类似的,有浮点数的运算过程会把操作数自动转型为`DOUBLE`类型。
小贴士: 有隐式类型转换,自然也有显式类型转换。在MySQL中,可以使用CAST函数完成显式地类型转换,就是我们明确指定要将特定的数值转换为某种特定类型,不过我们并不打算这个函数的使用,感兴趣的同学可以到文档中看看哈(我们不详细展开讲,自然是该知识点对于初学者并不是那么重要哈)。