SQL(Structured Query Language),被称为结构化查询语言,是目前主流的关系型数据库上执行数据操作、数据检索以及数据库维护所需要的标准语言。它将数据查询、数据操纵、事务控制、数据定义和数据控制集于一身。不分大小写,习惯上,关键字用大写,表名列命用小写。
创建数据库和表后,就可以针对表中的数据开展各种交互操作了。SELECT
语句功能强大、灵活,其数学理论基础是关系数据模型中对表对象的一组关系运算,即选择、投影和链接,可以查询表或视图获取所需数据。
语法格式为:
SELECT [ALL|DISTINCT] <目标列表达式1>,...,<目标列表达式N> FROM <表名或视图>,<表名N或视图N>
[WHERE<条件表达式>]
[GROUP BY <列名> [HAVING <分组后条件表达式>]]
[ORDER BY <列名> [ASC|DESC]]
[LIMIT [M,]N];
ALL
|DISTINCT
:可选项,返回结果集中的重复行。若没有指定这些选项,默认为ALL。若选择项为DISTINCT
,则会消除结果集中重复行。SELECT
子句:用于指定要显示的字段或表达式。FROM
子句:用于指定数据来源那些表或视图。WHERE
子句:可选项,用于对数据的筛选条件。GROUP BY
子句:可选项,用于数据的分组。HAVING
子句:可选项,用于分组后结果集的筛选条件。ORDER BY
子句:可选项,用于结果集的升序或降序排序,默认ASC升序。LIMIT
子句:可选项,用于查询结果集返回的记录数。SELECT
语句中,所有可选子句必须按罗列顺序使用。例如:HAVING
子句必须跟在GROUP BY
子句后面,如果不需要GROUP BY
子句,ORDER BY
子句就在WHERE
子句后面,以此向前推进。下面以school数据库的表为例,介绍SELECT
的用法:
班级表(class):
class_no(班级号) | class_count(总人数) | class_tech_no(班主任) |
---|---|---|
1 | 1 | 1 |
2 | 2 | 2 |
3 | 1 | 3 |
4 | 0 | 4 |
老师表(tech):
tech_no(老师号) | tech_name(老师名称) |
---|---|
1 | 谢老师 |
2 | 张老师 |
3 | 李老师 |
4 | 王老师 |
学生表(student):
student_no(学生号) | student_name(学生名称) | student_age(学生年龄) | class_no(班级号) |
---|---|---|---|
1 | 小明 | 12 | 1 |
2 | 小米 | 11 | 2 |
3 | 小敏 | 11 | 2 |
4 | 小蜜 | 10 | 3 |
仅设计一个表的查询。
语法格式:
SELECT 目标表达式列,....,目标表达式n FROM 表名;
比如:查询所有老师编号及姓名,执行结果如下所示:
mysql> select tech_no,tech_name from tech;
+---------+-----------+
| tech_no | tech_name |
+---------+-----------+
| 1 | 谢老师 |
| 2 | 张老师 |
| 3 | 李老师 |
| 4 | 王老师 |
+---------+-----------+
4 rows in set (0.00 sec)
可以看到,查询出来数据列的先后顺序和目标表达式列的顺序一致.
比如:查询学生的年龄,执行结果如下所示:
mysql> select student_age from student;
+-------------+
| student_age |
+-------------+
| 12 |
| 11 |
| 11 |
| 10 |
+-------------+
4 rows in set (0.00 sec)
可以看到包含了2条重复的记录,可以使用DISTINCT
关键字去重。
mysql> select distinct student_age from student;
+-------------+
| student_age |
+-------------+
| 12 |
| 11 |
| 10 |
+-------------+
3 rows in set (0.00 sec)
查询一个表中所有字段有两种方法:一种是使用SELECT
子句后面列出所有字段名;另一种是SELECT
子句后面直接使用*号(通配符),查询的结果与表中字段顺序一致。
比如:查询所有学生信息,执行结果如下所示:
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
4 rows in set (0.00 sec)
mysql> select student_no,student_name,student_age,class_no from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
4 rows in set (0.00 sec)
SELECT
子句中的目标列表达式不仅可以是字段名,也可以是表达式,还可以是字符串常量、函数等。比如:查询每个学生年龄距离成功还差多少岁,执行结果如下所示:
mysql> select student_name,18-student_age from student;
+--------------+----------------+
| student_name | 18-student_age |
+--------------+----------------+
| 小明 | 6 |
| 小米 | 7 |
| 小敏 | 7 |
| 小蜜 | 8 |
+--------------+----------------+
4 rows in set (0.00 sec)
AS
子句,来展示自己需要的列名。如果给表定义了别名,那么所有的列都要通过表的别名去使用。语法格式为:SELECT 目标表达式列 [AS] '字段别名',....,目标表达式n FROM 表名 [AS] '表的别名';
比如:将学生表的列转换为中文展示,执行结果如下所示:
mysql> select
-> stu.student_no as '学生编号',stu.student_name as '学生姓名',stu.student_age as '学生年龄',stu.class_no as '班级编号'
-> from student as stu;
+--------------+--------------+--------------+--------------+
| 学生编号 | 学生姓名 | 学生年龄 | 班级编号 |
+--------------+--------------+--------------+--------------+
| 1 | 小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+--------------+--------------+--------------+--------------+
4 rows in set (0.00 sec)
当查询指定数据时,在SELECT
子句中,WHERE
子句可以对数据进行筛选。语法格式为:
SELECT 目标列表达式1,.....,目标列表达式n
FROM 表名
WHERE 条件表达式;
WHERE
子句常用的查询条件如下所示:
查询条件 | 操作符 |
---|---|
比较 | =、<>、<、>、<=、>=、!= 等比较运算符 |
确定范围 | BETWEEN AND、NOT BETWEEN AND |
确定集合 | IN、NOT IN |
字符匹配 | LIKE、NOT LIKE |
空值 | IS NULL、IS NOT NULL |
多重值 | AND、OR |
比如:查询学生表年龄大于10岁小于等于12岁信息生,执行结果如下所示:
mysql> select * from student where student_age >10 and student_age<=12;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
+------------+--------------+-------------+----------+
3 rows in set (0.01 sec)
可以看到结果集为大于10岁(不包含10岁)且小于等于(包含)12岁的学生信息。
比如:现在要求查询年龄不等于10岁的学生信息,执行结果如下所示:
mysql> select * from student where student_age != 10;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
+------------+--------------+-------------+----------+
3 rows in set (0.00 sec)
可以看到除了10岁的学生信息其余的都展示出来了,符号“!=”可以用“<>”或NOT代替,有兴趣的同学可以自己去试下。
BETWEEN...AND
操作符。语法格式为:
WHERE 列 [NOT] BETWEEN 值1 AND 值2;
表示筛选某个列的取值范围。
比如:查询学生表年龄在10岁到12岁之间的信息。执行结果如下所示:
mysql> select * from student where student_age between 10 and 12;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
4 rows in set (0.00 sec)
与大于小于运算符不同,BETWEEN...AND
是包含条件值的,而且更加简洁,一般范围查值多用于时间筛选上。当然还有NOT BETWEENT
筛选不在这个范围的数据。
比如:查询学生年龄不在10岁到11岁之间的信息。执行结果如下所示:
mysql> select * from student where student_age not between 10 and 11;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
+------------+--------------+-------------+----------+
1 row in set (0.00 sec)
IN
可以查询多个指定范围的记录,当范围中有一条记录时,都会返回数据。比如:查询学生1、2、3、4班的学生数据。执行结果如下所示:
mysql> select * from student where class_no in (1,2,3,4);
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
4 rows in set (0.00 sec)
4班没有学生数据但是其他三个班级有所以其他三个班级的数据就展示出来了。相反,可以使用NOT IN
关键字过滤非此范围的数据。
比如:查询不是2班的学生信息。执行结果如下所示:
mysql> select * from student where class_no not in (2);
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 小明 | 12 | 1 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
2 rows in set (0.00 sec)
语法格式为:
WHERE [NOT] LIKE '匹配串'[ESCAPE '换码字符'];
其中,匹配串不仅仅包含查询的值,可能还包含通配符,MySQL中通配符有两种:’%‘和’_’。其中’%‘表示任意长度字符串;’ _ '(横线)表示任意单个字符。
比如:稍微改下数据,查询学生姓为“小”开头的学生,执行结果如下所示:
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
4 rows in set (0.00 sec)
mysql> select * from student where student_name like '小%';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
3 rows in set (0.00 sec)
比如:查询学生姓不为“小”开头的学生信息,执行结果如下所示:
mysql> select * from student where student_name not like '小%';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
+------------+--------------+-------------+----------+
1 row in set (0.00 sec)
比如:查询学生姓为“张”开头的,任意两个姓的学生,数据稍微改动,执行结果如下所示:
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
5 rows in set (0.00 sec)
mysql> select * from student where student_name like '张__';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
+------------+--------------+-------------+----------+
1 row in set (0.00 sec)
WHERE
条件后。语法格式为:WHERE [NOT] [REGEXP | RLIKE] 正则表达式;
字符匹配列表:
选项 | 说明 | 例子 | 匹配值示例 |
---|---|---|---|
<字符串> | 匹配包含指定字符串的文本 | ‘fa’ | fab,afa |
[ ] | 匹配[ ]中任意一个值字符 | ‘[ab]’ | bay,cba |
[^ ] | 匹配不在[ ]中任意一个字符 | ‘[^ab]’ | cqs,tst |
^ | 匹配文本的开始字符 | ‘^b’ | bab,boo |
$ | 匹配文本的结尾字符 | ‘c$’ | abc,ooc |
. | 匹配任意单个字符 | ‘b.t’ | bet,bit |
* | 匹配0个或多个*前面指定字符 | ‘f*n’ | fn,fcn |
+ | 匹配+前面1次或多次字符 | ‘ab+’ | abb,abqq |
{n} | 匹配前面字符至少n次 | ‘b{2}’ | bb,bbbb |
比如:查询学生表姓名带有“小明”的信息,执行结果如下所示:
mysql> select * from student where student_name regexp '小明';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
2 rows in set (0.01 sec)
等价于
mysql> select * from student where student_name like '%小明%';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
2 rows in set (0.00 sec)
尽管语句看上去很相似,但是like必须加上通配符才可以查到数据。
IS NULL
关键字查询空数据。比如:查询学生表班级号为NULL
的学生信息,数据稍微修改。执行结果如下所示:
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
+------------+--------------+-------------+----------+
6 rows in set (0.00 sec)
mysql> select * from student where class_no is null;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 6 | 张小明二 | 12 | NULL |
+------------+--------------+-------------+----------+
1 row in set (0.00 sec)
相反,可以查询班级号不为NULL
的学生信息,执行结果如下所示:
mysql> select * from student where class_no is not null;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
| 2 | 小米 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
5 rows in set (0.00 sec)
注意:IS NULL
不能用 = NULL代替,IS NOT NULL
也不能用 != NULL 代替,这样查询不到数据。
AND
和OR
用来连接多个查询条件,AND
只有满足所有条件时才会返回数据;OR
只有满足其中一个就返回数据,AND
优先级高于OR
,可以通过括号改变优先级。比如:查询学生表年龄等于12并且班级为1班的学生信息,执行结果如下所示:
mysql> select * from student where student_age='12' and class_no='1';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 12 | 1 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
2 rows in set (0.00 sec)
比如:查询学生表2班或4班的学生信息,执行结果如下所示:
mysql> select * from student where class_no='3' or class_no='4';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
1 row in set (0.00 sec)
由于4班没有学生信息。所以只返回了3班的学生信息,OR
和IN
可以实现相同功能。
比如:查询1班或2班两个班级中,年龄大于12的学生信息,数据稍作修改,执行结果如下所示:
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
+------------+--------------+-------------+----------+
6 rows in set (0.00 sec)
mysql> select * from student where (class_no = '1' or class_no = '2') and student_age > '12';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
+------------+--------------+-------------+----------+
2 rows in set (0.00 sec)
可以看到括号改变了OR
的优先级,如果不加括号,就会变成两条OR
条件,执行结果如下所示:
mysql> select * from student where class_no = '1' or class_no = '2' and student_age > '12';
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
3 rows in set (0.00 sec)
在SELECT
子句中,可以使用GROUP BY
将查询结果集进行分组,值相等的为一组;HAVING
子句可以对分组的结果进行过滤。语法格式为:
GROUP BY <字段名> [HAVING <条件表达式>];
比如:查询每个班级学生总数。执行结果如下所示:
mysql> select class_no,count(*) from student group by class_no;
+----------+----------+
| class_no | count(*) |
+----------+----------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
| NULL | 1 |
+----------+----------+
4 rows in set (0.00 sec)
聚合函数再之前已经讲解过,可以去看第二章的内容,这里不做重复讲解。由于分组的值中有NULL值,所以NULL值会单独做一个分组。
比如:要统计每个班级平均年龄大于10岁以上的班级学生总数。执行结果如下所示:
mysql> select class_no,count(*) from student group by class_no having sum(student_age)/count(*) >10;
+----------+----------+
| class_no | count(*) |
+----------+----------+
| 1 | 2 |
| 2 | 2 |
| NULL | 1 |
+----------+----------+
3 rows in set (0.00 sec)
每个班级的平均年龄需要先加上每个班级学生年龄再除以班级总人数得到结果后,再判断大于10的平均年龄。
在SELECT
子句中,可以使用ORDER BY
将查询结果集中记录按一个或多个字段值的升序或降序排序。关键字ASC
按升序排列;DESC
按降序排列,默认值为ASC
。语法格式为:
ORDER BY <字段名> [ASC|DESC];
比如:查询学生表所有数据,按班级号升序排列。执行结果如下所示:
mysql> select * from student order by class_no asc;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 6 | 张小明二 | 12 | NULL |
| 1 | 张小明 | 13 | 1 |
| 5 | 张小明一 | 12 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
6 rows in set (0.00 sec)
可以看到班级号字段按照升序进行了排列,NULL值在排序中为最小,所以排在最前面。ORDER BY
语句可以同时对多个字段按从左到右依次进行排序。
比如:查询学生表所有数据,让学生年龄升序,让班级号降序排列。执行结果如下所示:
mysql> select * from student order by student_age asc,class_no desc;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 4 | 小蜜 | 10 | 3 |
| 3 | 小敏 | 11 | 2 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
| 2 | 小米 | 13 | 2 |
| 1 | 张小明 | 13 | 1 |
+------------+--------------+-------------+----------+
6 rows in set (0.00 sec)
可以看到查询的结果最先以第一个进行排序后再第一个排序规则上进行第二个排序。
当查询的数据量太多时,为了方便对数据进行浏览,可以使用LIMIT
子句限制结果集的行数。语法格式为:
LIMIT 初始位置,记录数;
比如:查询学生表年龄最大的三条记录。执行结果如下所示:
mysql> select * from student order by student_age desc limit 0,3;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
3 rows in set (0.00 sec)
先对数据进行降序排列,然后使用LIMIT
子句对数据进行限制,这样就得到目标结果。
前面介绍都是针对一个表进行的。如果涉及多个表,则称为连接查询。连接查询包括交叉查询、内连接和外连接。可以通过多个表的相同字段进行连接查询。
交叉链接(CROSS JOIN)又称笛卡尔积,即把两张表每一行连接起来,返回所有可能搭配的结果。语法格式为:
SELECT * FROM 表1 CROSS JOIN 表2;
或
SELECT * FROM 表1,表2;
比如:老师表和班级表进行交叉连接查询,老师表和班级各有4条数据,一共4*4=16条数据。执行结果如下所示:
mysql> select * from class,tech;
+----------+-------------+---------------+---------+-----------+
| class_no | class_count | class_tech_no | tech_no | tech_name |
+----------+-------------+---------------+---------+-----------+
| 4 | 0 | 4 | 1 | 谢老师 |
| 3 | 1 | 3 | 1 | 谢老师 |
| 2 | 2 | 2 | 1 | 谢老师 |
| 1 | 1 | 1 | 1 | 谢老师 |
| 4 | 0 | 4 | 2 | 张老师 |
| 3 | 1 | 3 | 2 | 张老师 |
| 2 | 2 | 2 | 2 | 张老师 |
| 1 | 1 | 1 | 2 | 张老师 |
| 4 | 0 | 4 | 3 | 李老师 |
| 3 | 1 | 3 | 3 | 李老师 |
| 2 | 2 | 2 | 3 | 李老师 |
| 1 | 1 | 1 | 3 | 李老师 |
| 4 | 0 | 4 | 4 | 王老师 |
| 3 | 1 | 3 | 4 | 王老师 |
| 2 | 2 | 2 | 4 | 王老师 |
| 1 | 1 | 1 | 4 | 王老师 |
+----------+-------------+---------------+---------+-----------+
16 rows in set (0.00 sec)
这样查询的结果集会非常的大,耗时长,避免使用这种查询。
内连接(INNER JOIN)在查询中设置连接条件来移除交叉连接查询结果集中某些无意义的数据行。内连接有两种语法结构:显示语法结构和隐式语法结构。语法结构如下:
显示语法结构
SELECT <目标列表达式1>,...<目标列表达式N>
FROM <表名1或视图1> [INNER] JOIN <表名2或视图2> [[INNER] JOIN <表名N或视图N>]
ON 连接条件
[WHERE<条件表达式>];
隐式语法结构
SELECT <目标列表达式1>,...<目标列表达式N>
FROM <表名1或视图1>,<表名N或视图N>
WHERE<条件表达式>;
显示语法结构通过ON
子句来设置表的连接条件;隐式语法结构通过WHERE
子句来设置表的连接条件。表与表之间的数据过滤条件都在WHERE
子句中。
语法格式为:
[<表名1>].字段名 <比较运算符> [<表名2>].<字段名>
当比较运算符“=”时表示等值连接,使用其他运算符为非等值连接。
比如:查询每个班级的班主任信息。执行结果如下所示:
mysql> select * from class inner join tech on class.class_tech_no = tech.tech_no;
+----------+-------------+---------------+---------+-----------+
| class_no | class_count | class_tech_no | tech_no | tech_name |
+----------+-------------+---------------+---------+-----------+
| 1 | 1 | 1 | 1 | 谢老师 |
| 2 | 2 | 2 | 2 | 张老师 |
| 3 | 1 | 3 | 3 | 李老师 |
| 4 | 0 | 4 | 4 | 王老师 |
+----------+-------------+---------------+---------+-----------+
4 rows in set (0.00 sec)
或
mysql> select * from class,tech where class.class_tech_no = tech.tech_no;
+----------+-------------+---------------+---------+-----------+
| class_no | class_count | class_tech_no | tech_no | tech_name |
+----------+-------------+---------------+---------+-----------+
| 1 | 1 | 1 | 1 | 谢老师 |
| 2 | 2 | 2 | 2 | 张老师 |
| 3 | 1 | 3 | 3 | 李老师 |
| 4 | 0 | 4 | 4 | 王老师 |
+----------+-------------+---------------+---------+-----------+
4 rows in set (0.00 sec)
这样每个班级对应班主任信息,通过老师编号字段连接起来,得到最终结果集。
比如:查询班级表的信息。执行结果如下所示:
mysql> select * from class c1 inner join class c2 on c1.class_no = c2.class_no;
+----------+-------------+---------------+----------+-------------+---------------+
| class_no | class_count | class_tech_no | class_no | class_count | class_tech_no |
+----------+-------------+---------------+----------+-------------+---------------+
| 1 | 1 | 1 | 1 | 1 | 1 |
| 2 | 2 | 2 | 2 | 2 | 2 |
| 3 | 1 | 3 | 3 | 1 | 3 |
| 4 | 0 | 4 | 4 | 0 | 4 |
+----------+-------------+---------------+----------+-------------+---------------+
4 rows in set (0.00 sec)
比如:查询班级学生的信息。执行结果如下所示:
mysql> select * from student stu natural join class cla;
+----------+------------+--------------+-------------+-------------+---------------+
| class_no | student_no | student_name | student_age | class_count | class_tech_no |
+----------+------------+--------------+-------------+-------------+---------------+
| 1 | 1 | 张小明 | 13 | 1 | 1 |
| 2 | 2 | 小米 | 13 | 2 | 2 |
| 2 | 3 | 小敏 | 11 | 2 | 2 |
| 3 | 4 | 小蜜 | 10 | 1 | 3 |
| 1 | 5 | 张小明一 | 12 | 1 | 1 |
+----------+------------+--------------+-------------+-------------+---------------+
5 rows in set (0.00 sec)
可以看到自然连接是不需要指定连接条件的,系统会根据相同字段名进行连接。
连接查询是查询多个表中相关联的行,内连接查询只返回查询结果集合中复合条件的数据;外连接将两张表分为基表和参考表,以基表为准返回所有数据。
比如:查询班级学生信息,可以参考自然连接查询的结果集进行对比,查看区别。执行结果如下所示:
mysql> select * from student stu left join class cla on stu.class_no= cla.class_no;
+------------+--------------+-------------+----------+----------+-------------+---------------+
| student_no | student_name | student_age | class_no | class_no | class_count | class_tech_no |
+------------+--------------+-------------+----------+----------+-------------+---------------+
| 1 | 张小明 | 13 | 1 | 1 | 1 | 1 |
| 2 | 小米 | 13 | 2 | 2 | 2 | 2 |
| 3 | 小敏 | 11 | 2 | 2 | 2 | 2 |
| 4 | 小蜜 | 10 | 3 | 3 | 1 | 3 |
| 5 | 张小明一 | 12 | 1 | 1 | 1 | 1 |
| 6 | 张小明二 | 12 | NULL | NULL | NULL | NULL |
+------------+--------------+-------------+----------+----------+-------------+---------------+
6 rows in set (0.00 sec)
比如:查询班级学生信息,可以参考外连接查询的结果集进行对比,查看区别。执行结果如下所示:
mysql> select * from student stu right join class cla on stu.class_no= cla.class_no;
+------------+--------------+-------------+----------+----------+-------------+---------------+
| student_no | student_name | student_age | class_no | class_no | class_count | class_tech_no |
+------------+--------------+-------------+----------+----------+-------------+---------------+
| 5 | 张小明一 | 12 | 1 | 1 | 1 | 1 |
| 1 | 张小明 | 13 | 1 | 1 | 1 | 1 |
| 3 | 小敏 | 11 | 2 | 2 | 2 | 2 |
| 2 | 小米 | 13 | 2 | 2 | 2 | 2 |
| 4 | 小蜜 | 10 | 3 | 3 | 1 | 3 |
| NULL | NULL | NULL | NULL | 4 | 0 | 4 |
+------------+--------------+-------------+----------+----------+-------------+---------------+
6 rows in set (0.00 sec)
子查询也称嵌套查询,是将一个查询语句嵌套在另外一个查询语句的WHERE
子句或HAVING
短语中,先计算子查询,然后子查询的结果作为父查询的过滤条件。
IN
关键字内层查询语句仅仅返回一个数据列,其值提供给外层查询进行比较操作。
比如:查询每个班级的学生。执行结果如下所示:
mysql> select class_no from class;
+----------+
| class_no |
+----------+
| 1 |
| 2 |
| 3 |
| 4 |
+----------+
4 rows in set (0.02 sec)
mysql> select * from student where class_no in (select class_no from class);
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
5 rows in set (0.00 sec)
虽然表结构中有class_no字段,为了达到演示效果,特别讲解。当然也可以给定固定参数。执行结果如下所示:
mysql> select * from student where class_no in ('1','2','3','4');
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
+------------+--------------+-------------+----------+
5 rows in set (0.00 sec)
有了IN
关键字查询,当然也有NOT IN关键字查询。比如:查询不在班号1、2、4的每个班级学生。执行结果如下所示:
mysql> select * from student where class_no not in ('1','2','4');
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 4 | 小蜜 | 10 | 3 |
+------------+--------------+-------------+----------+
1 row in set (0.00 sec)
在子查询使用比较运算符进行连接。返回的值是唯一单条数据时,可以用<、>、<=、>=、=、<>、!=等运算符构造子查询。
比如:查询“谢老师”所在班级。执行结果如下所示:
mysql> select * from class where class_tech_no = (select tech_no from tech where tech_name='谢老师');
+----------+-------------+---------------+
| class_no | class_count | class_tech_no |
+----------+-------------+---------------+
| 1 | 1 | 1 |
+----------+-------------+---------------+
1 row in set (0.01 sec)
使用关键字EXISTS
构造子查询时,系统会判断子查询结果集是否为空,如果不为空返回TRUE,否则返回FALSE,外层语句将不进行查询。
比如:分别查询“谢老师”和“刘老师”的所在班级。执行结果如下所示:
mysql> select * from class where exists (select tech_no from tech where tech_name='谢老师');
+----------+-------------+---------------+
| class_no | class_count | class_tech_no |
+----------+-------------+---------------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 1 | 3 |
| 4 | 0 | 4 |
+----------+-------------+---------------+
4 rows in set (0.00 sec)
mysql> select * from class where exists (select tech_no from tech where tech_name='刘老师');
Empty set (0.00 sec)
与EXISTS
关键字对应的是NOT EXISTS
。返回结果相反,如果为空返回TRUE,否则返回FALSE,外层语句将不进行查询。
比如:分别查询“谢老师”和“刘老师”的所在班级。执行结果如下所示:
mysql> select * from class where not exists (select tech_no from tech where tech_name='谢老师');
Empty set (0.00 sec)
mysql> select * from class where not exists (select tech_no from tech where tech_name='刘老师');
+----------+-------------+---------------+
| class_no | class_count | class_tech_no |
+----------+-------------+---------------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 1 | 3 |
| 4 | 0 | 4 |
+----------+-------------+---------------+
4 rows in set (0.00 sec)
使用UNION
关键字可以把多个SELECT
语句不同表,相同列名、列数返回到一个结果集中,这种查询方式称为并(UNION)运算或联合运算。语法格式为:
SELECT <目标列表达式1>,...,<目标列表达式N> FROM <表名或视图>
[WHERE<条件表达式>]
[GROUP BY <列名> [HAVING <分组后条件表达式>]]
[ORDER BY <列名> [ASC|DESC]]
[LIMIT [M,]N]
UNION [ALL]
SELECT <目标列表达式1>,...,<目标列表达式N> FROM <表名或视图>
[WHERE<条件表达式>]
[GROUP BY <列名> [HAVING <分组后条件表达式>]]
[ORDER BY <列名> [ASC|DESC]]
[LIMIT [M,]N]
不用关键字ALL
,执行时去掉重复记录(通过主键去判断唯一)返回的行都是唯一的,使用关键字ALL
,不会去掉重复记录,也不会对结果自动排序。
比如:修改表进行修改后,使用联合查询,查询每个班级的学生信息。执行结果如下所示:
mysql> select * from student where class_no=1
-> union
-> select * from student where class_no=2;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 5 | 张小明一 | 12 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
+------------+--------------+-------------+----------+
4 rows in set (0.00 sec)
mysql> select * from student where class_no=1
-> union all
-> select * from student where class_no=2;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 5 | 张小明一 | 12 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 3 | 小敏 | 11 | 2 |
+------------+--------------+-------------+----------+
5 rows in set (0.00 sec)
使用UNION关键字需要注意以下几点:
SELECT
语句的目标列名会被当做UNION
语句的结果集名称。SELECT
语句组成,彼此之间用UNION
关键字分隔。UNION
语句的每个SELECT
语句包含相同列、表达式或聚合函数。ORDER BY
语句或LIMIT
语句,并且只能在最后一条语句中使用。MySQL提供了丰富的数据库更新操作语句,包括插入INSERT
语句、修改UPDATE
语句和删除DELETE
语句。本章详细讲解如何使用这些语句操作数据。
查询数据的时候,表里必须有数据。MySQL使用INSERT
或REPLACE
语句向表里插入新的数据,插入方式有:插入完整数据、插入一部分数据、插入多条数据、插入另一个查询结果等。
语法格式为:
INSERT INTO 表名(列名1,...列名N) VALUES(值1,...,值N);
使用该语句要保证字段必须存在、字段和值的数量一致、值的类型和字段类型匹配。
向表中插入值的方法有两种:一种是指定所有字段名;另一种是不指定字段名,不指定字段名时,要为表里每个字段指定值,顺序和表字段的顺序一致。
比如:向学生表里插入新的学生记录。执行结果如下所示:
mysql> insert into student(student_no,student_name,student_age) values(7,'朱晓明',14);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
| 7 | 朱晓明 | 14 | NULL |
+------------+--------------+-------------+----------+
8 rows in set (0.00 sec)
指定字段名的方式,可以忽略掉默认值为NULL的字段,只传已经定义好字段的值。
比如:不指定字段名,向学生表里插入新的学生记录。执行结果如下所示:
mysql> insert into student values(8,'周润法',14,null);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
| 7 | 朱晓明 | 14 | NULL |
| 8 | 周润法 | 14 | NULL |
+------------+--------------+-------------+----------+
9 rows in set (0.00 sec)
如果不传字段名,传值就必须按字段顺序,进行传入,否则就会报错。执行结果如下所示:
mysql> insert into student values(9,'朱明',14);
ERROR 1136 (21S01): Column count doesn't match value count at row 1
插入多条数据,指定好字段名后,只需用逗号分隔开的方式追加值。语法格式为:
INSERT INTO 表名(列名1,...列名N) VALUES(值1,...,值N),....,(值1,...,值N);
比如:学校新入职几名老师。执行结果如下所示:
mysql> select * from tech;
+---------+-----------+
| tech_no | tech_name |
+---------+-----------+
| 1 | 谢老师 |
| 2 | 张老师 |
| 3 | 李老师 |
| 4 | 王老师 |
+---------+-----------+
4 rows in set (0.00 sec)
mysql> insert into tech(tech_no,tech_name) values('5','刘爱民'),('6','李建辉');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from tech;
+---------+-----------+
| tech_no | tech_name |
+---------+-----------+
| 1 | 谢老师 |
| 2 | 张老师 |
| 3 | 李老师 |
| 4 | 王老师 |
| 5 | 刘爱民 |
| 6 | 李建辉 |
+---------+-----------+
6 rows in set (0.00 sec)
INSERT
语句插入记录时,不仅可以指定插入记录的值,还可以将SELECT
语句查询的结果插入到表中。语法格式为:
INSERT INTO 表名(列名1,...列名N)
SELECT <目标列表达式1>,...,<目标列表达式N> FROM <表名或视图>
[WHERE<条件表达式>];
比如:为student新建一个student_copy的表进行备份,将student表的数据放到备份表中去。执行结果如下所示:
mysql> create table student_copy(
-> student_no int,
-> student_name varchar(20),
-> student_age bigint,
-> class_no int
-> );
Query OK, 0 rows affected (0.03 sec)
mysql> insert into student_copy(student_no,student_name,student_age,class_no)
-> select * from student;
Query OK, 9 rows affected (0.01 sec)
Records: 9 Duplicates: 0 Warnings: 0
mysql> select * from student_copy;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 13 | 1 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
| 7 | 朱晓明 | 14 | NULL |
| 8 | 周润法 | 14 | NULL |
+------------+--------------+-------------+----------+
9 rows in set (0.00 sec)
如果一个表有PRIMARY KEY
或 UNIQUE
约束,而待插入的数据行包含相同数据,INSERT
语句查询就会报错,则可以使用REPLACE
语句来实现。使用REPLACE
语句会在插入数据之前将冲突的就数据删除,保证新数据能正常插入。语法格式为:
REPLACE INTO 表名(列名1,...列名N) VALUES(值1,...,值N);
比如:学生表中“学生张小明”的年龄和班级填写错误重新插入。执行结果如下:
mysql> replace into student(student_no,student_name,student_age,class_no) values(1,'张小明',14,4);
Query OK, 2 rows affected (0.01 sec)
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 14 | 4 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
| 7 | 朱晓明 | 14 | NULL |
| 8 | 周润法 | 14 | NULL |
+------------+--------------+-------------+----------+
8 rows in set (0.00 sec)
如果某个字段定义了外键,使用REPLACE
语句也会报错。
在MySQL中,可以使用UPDATE
语句修改一个表或多个表中的数据。语法格式为:
UPDATE 表名 SET 列1=值1,...,列N=值N [WHERE <条件表达式>];
SET
子句表示要修改表的字段名的值,修改多个字段值用逗号分隔开;WHERE
子句是可选项,若没有WHERE
则会修改表中所有数据行。
比如:将学生“朱晓明”、“周润法”分配到班号为1的班级中去。执行结果如下所示:
mysql> update student set class_no='1' where student_name in ('朱晓明','周润法');
Query OK, 2 rows affected (0.01 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 14 | 4 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 5 | 张小明一 | 12 | 1 |
| 6 | 张小明二 | 12 | NULL |
| 7 | 朱晓明 | 14 | 1 |
| 8 | 周润法 | 14 | 1 |
+------------+--------------+-------------+----------+
8 rows in set (0.00 sec)
删除一行或多行可以使用DELETE
语句。语法格式为:
DELETE FROM 表名 [WHERE <条件表达式>];
WHERE
子句为可选项,如果没有则删除全部数据,谨慎用这种方法操作。
比如:删除学生表中学生姓名“张小明一”和“张小明二”。执行结果如下所示:
mysql> delete from student where student_name like '张小明_';
Query OK, 2 rows affected (0.00 sec)
mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 14 | 4 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 7 | 朱晓明 | 14 | 1 |
| 8 | 周润法 | NULL | 1 |
+------------+--------------+-------------+----------+
6 rows in set (0.00 sec)
删除所有不仅可以使用不加条件的DELETE
语句,还可以使用TRUNCATE
语句删除,不过TRUNCATE
不是删除表的数据,而是直接把表删了重新建立一个表。语法格式为:
TRUNCATE [TABLE] 表名;
比如:删除student_copy的备份表全部数据。执行结果如下所示:
mysql> select * from student_copy;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
| 1 | 张小明 | 14 | 4 |
| 2 | 小米 | 13 | 2 |
| 3 | 小敏 | 11 | 2 |
| 4 | 小蜜 | 10 | 3 |
| 7 | 朱晓明 | 14 | 1 |
| 8 | 周润法 | NULL | 1 |
+------------+--------------+-------------+----------+
6 rows in set (0.00 sec)
mysql> truncate student_copy;
Query OK, 0 rows affected (0.03 sec)
mysql> select * from student_copy;
Empty set (0.00 sec)
通过查询可知表中所有记录都被删除成功。在使用TRUNCATE
语句删除时需要注意几点:
TRUNCATE
语句不带WHERE
子句和DELETE
子句一样,删除表中所有数据,且无法回复,谨慎使用。TRUNCATE
语句后,表中AUTO_INCREMENT
计数器将被重置为初始值。上一篇:MySQL数据库程序设计(二)
下一篇:MySQL数据库程序设计(四)