高级数据操作

2019-04-13
loubth

新增数据

多数据插入

只要写一次insert指令,但是可以直接插入多条记录

基本语法:INSERT INTO 表名[(字段列表)] VALUES(值列表1),(值列表2),...;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> DESC my_gbk;
+-------+----------------+------+-----+---------+-------+
| Field | Type           | Null | Key | Default | Extra |
+-------+----------------+------+-----+---------+-------+
| name  | varchar(20000) | YES  |     | NULL    |       |
+-------+----------------+------+-----+---------+-------+
1 row in set (0.00 sec)
 
mysql> SELECT * FROM my_gbk;
Empty set (0.00 sec)
 
mysql> INSERT INTO my_gbk VALUES('Tom'),('Jack'),('John');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0
 
mysql> SELECT * FROM my_gbk;
+------+
| name |
+------+
| Tom  |
| Jack |
| John |
+------+
3 rows in set (0.00 sec)

主键冲突

主键冲突:在有的表中,使用的是业务主键(字段有业务含义),但是往往在进行数据插入的时候,又不确定数据表中是否已经存在对应的主键

如果待插入的记录的主键在数据库已经存在,此时数据库系统就会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> SELECT * FROM my_student;
+---------+----------+
| stu_id  | stu_name |
+---------+----------+
| stu0001 | 张三     |
| stu0002 | 张四     |
| stu0003 | 张五     |
| stu0004 | 张六     |
+---------+----------+
4 rows in set (0.00 sec)
 
mysql> INSERT INTO my_student VALUES('stu0004','小婷');
ERROR 1062 (23000): Duplicate entry 'stu0004' for key 'PRIMARY'

那么这个问题该怎么解决呢?mysql中提供了两种解决方案

  1. 主键中突更新:

    类似插入数据语法,如果插入的过程中主键冲突,那么采用更新方法

    INSERT INTO 表名[(字段列表)] VALUES(值列表) ON DUPLICATE KEY UPDATE 字段=新值;

    提示:duplicate是“重复”的意思

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    mysql> DESC my_student;
    +----------+-------------+------+-----+---------+-------+
    | Field    | Type        | Null | Key | Default | Extra |
    +----------+-------------+------+-----+---------+-------+
    | stu_id   | varchar(10) | NO   | PRI | NULL    |       |
    | stu_name | varchar(10) | NO   |     | NULL    |       |
    +----------+-------------+------+-----+---------+-------+
    2 rows in set (0.04 sec)
     
    mysql> SELECT * FROM my_student;
    +---------+----------+
    | stu_id  | stu_name |
    +---------+----------+
    | stu0001 | 张三     |
    | stu0002 | 张四     |
    | stu0003 | 张五     |
    | stu0004 | 张六     |
    +---------+----------+
    4 rows in set (0.00 sec)
     
    mysql> INSERT INTO my_student VALUES('stu0004','小婷') ON DUPLICATE KEY UPDATE stu_name='小婷';
    Query OK, 2 rows affected (0.00 sec)
     
    mysql> SELECT * FROM my_student;
    +---------+----------+
    | stu_id  | stu_name |
    +---------+----------+
    | stu0001 | 张三     |
    | stu0002 | 张四     |
    | stu0003 | 张五     |
    | stu0004 | 小婷     |
    +---------+----------+
    4 rows in set (0.00 sec)

    如果仔细观察我们会发现一个问题,为啥更新了一条影响行数为2呢?这个问题 官方文档给出了说明

    With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values. If you specify the CLIENT_FOUND_ROWS flag to the mysql_real_connect() C API function when connecting to mysqld, the affected-rows value is 1 (not 0) if an existing row is set to its current values.

    翻译如下

    对于重复键更新,如果将行插入为新行,则每一行的受影响行值为1,如果更新现有行,则为2,如果将现有行设置为其当前值,则为0。如果在连接到mysqld时向mysql_real_connect() C API函数指定CLIENT_FOUND_ROWS标志,如果将现有行设置为其当前值,则受影响的行值为1(而不是0)。

    其实只要这样想就好了,这是为了区分到底是插入了还是更新,返回1表示插入成功,2表示更新成功

  2. 主键冲突替换:

    当主键冲突之后,干掉原来的数据,重新插入一条新的进去,它的语法和insert极其相似

    REPLACE INTO 表名[(字段列表)] VALUES(值列表);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    mysql> SELECT * FROM my_student;
    +---------+----------+
    | stu_id  | stu_name |
    +---------+----------+
    | stu0001 | 张三     |
    | stu0002 | 张四     |
    | stu0003 | 张五     |
    | stu0004 | 小婷     |
    +---------+----------+
    4 rows in set (0.00 sec)
     
    mysql> REPLACE INTO my_student VALUES('stu0001','夏洛');
    Query OK, 2 rows affected (0.00 sec)
     
    mysql> SELECT * FROM my_student;
    +---------+----------+
    | stu_id  | stu_name |
    +---------+----------+
    | stu0001 | 夏洛     |
    | stu0002 | 张四     |
    | stu0003 | 张五     |
    | stu0004 | 小婷     |
    +---------+----------+
    4 rows in set (0.00 sec)

蠕虫复制

蠕虫复制:一分为二,成倍的增加。从已有的数据中获取数据,并且将获取到的数据插入到数据表中

基本语法:INSERT INTO 表名[(字段列表)] SELECT 字段列表 FROM 表名;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
mysql> CREATE TABLE my_simple(
    ->   name char(1) NOT NULL
    -> );
Query OK, 0 rows affected (0.04 sec)
 
mysql> INSERT INTO my_simple VALUES('a'),('b'),('c'),('d');
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0
 
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
+------+
4 rows in set (0.00 sec)
 
mysql> -- 蠕虫复制
mysql> INSERT INTO my_simple(name) SELECT name FROM my_simple;
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0
 
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+
8 rows in set (0.00 sec)
 
mysql> -- 蠕虫复制
mysql> INSERT INTO my_simple(name) SELECT name FROM my_simple;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0
 
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+
16 rows in set (0.00 sec)

注意:

  1. 蠕虫复制的确通常是重复数据,没有太大业务意义:可以在短期内快速增加表的数据量,从而可以测试表的压力,还可以通过大量数据来测试表的效率(索引)
  2. 蠕虫复制虽好,但是要注意主键冲突

提示:蠕虫复制可以配合创建表语句(使用like关键字可以拷贝表结构“CREATE TABLE 新表名 LIKE 待复制表名;”)来快速拷贝一张已经存在的表


更新数据

1、在更新数据的时候,特别要注意:通常一定是跟随条件更新

基本语法:UPDATE 表名 SET 字段名=新值 WHERE 判断条件;

2、如果没有条件,是全表更新数据。但是可以使用LIMIT来显示更新的数量,

基本语法:UPDATE 表名 SET 字段名=新值 [WHERE 判断条件] LIMIT 数量;

示例,将表中前4个a改成e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+
32 rows in set (0.00 sec)
 
mysql> UPDATE my_simple SET name='e' WHERE name='a' LIMIT 4;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4  Changed: 4  Warnings: 0
 
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| e    |
| b    |
| c    |
| d    |
| e    |
| b    |
| c    |
| d    |
| e    |
| b    |
| c    |
| d    |
| e    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+
32 rows in set (0.00 sec)

删除数据

1、删除数据的时候尽量不要全部删除,应该使用where进行判定

2、删除数据的时候可以使用limit来限制要删除的具体数量

3、Delete删除数据的时候无法重置auto_increment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
mysql> DELETE FROM my_auto;
Query OK, 0 rows affected (0.00 sec)
 
mysql> SHOW CREATE TABLE my_auto;
+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table   | Create Table                                                                                                                                                                                                                                   |
+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| my_auto | CREATE TABLE `my_auto` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL COMMENT '用户名',
  `pass` varchar(50) NOT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8      |
+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
 
mysql> INSERT INTO my_auto VALUES(NULL,'ZhangSan',321456);
Query OK, 1 row affected (0.01 sec)
 
mysql> SELECT * FROM my_auto;
+----+----------+--------+
| id | name     | pass   |
+----+----------+--------+
|  6 | ZhangSan | 321456 |
+----+----------+--------+
1 row in set (0.00 sec)

可以看到,即使已经将表中的数据全部删除,再插入一个数据id并不会恢复到“1”

这个问题该怎么解决呢?

Mysql有一个能够重置表选项中的自增长的语法:

TRUNCATE 表名;

提示:truncate是“截断”的意思

可以理解成直接将表先删除再创建一个一样结构的表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
mysql> TRUNCATE my_auto;
Query OK, 0 rows affected (0.08 sec)
 
mysql> SHOW CREATE TABLE my_auto;
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table   | Create Table                                                                                                                                                                                                                  |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| my_auto | CREATE TABLE `my_auto` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL COMMENT '用户名',
  `pass` varchar(50) NOT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8      |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
 
mysql> INSERT INTO my_auto VALUES(NULL,'ZhangSan',321456);
Query OK, 1 row affected (0.00 sec)
 
mysql> SELECT * FROM my_auto;
+----+----------+--------+
| id | name     | pass   |
+----+----------+--------+
|  1 | ZhangSan | 321456 |
+----+----------+--------+
1 row in set (0.00 sec)

查询数据

完整语法:SELECT 选项 字段列表 FROM 数据源 WHERE 条件 GROUP BY 分组 HAVING 条件 ORDER BY 排序 LIMIT 限制;

Select的选项:系统该如何对待查询得到的结果

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+
8 rows in set (0.01 sec)
 
mysql> SELECT ALL * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+
8 rows in set (0.00 sec)
 
mysql> SELECT DISTINCT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
+------+
4 rows in set (0.00 sec)

写字段列表的时候要注意,有的时候需要从多张表获取数据,在获取数据的时候,可能存在不同表中有同名的字段,需要将同名的字段命名成不同名的,此时需要使用别名alias

基本语法:字段名 [AS] 别名;

注意:同一个字段可以同时使用不同别名取出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+
8 rows in set (0.00 sec)
 
mysql> SELECT DISTINCT name AS name1,name AS name2 FROM my_simple;
+-------+-------+
| name1 | name2 |
+-------+-------+
| a     | a     |
| b     | b     |
| c     | c     |
| d     | d     |
+-------+-------+
4 rows in set (0.01 sec)

FROM+数据源

数据源为前面的查询提供数据,数据源只要是一个符合二维表结构的数据即可

单表数据

FROM后面只跟一张表

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
| a    |
| b    |
| c    |
| d    |
+------+

多表数据

FROM后面跟不只一张表

基本语法:FROM 表1,表2,...;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
mysql> SELECT * FROM my_simple;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
+------+
4 rows in set (0.00 sec)
 
mysql> SELECT * FROM my_int;
+------+
| num  |
+------+
| 0001 |
| 0002 |
+------+
2 rows in set (0.00 sec)
 
mysql> SELECT * FROM my_simple,my_int;
+------+------+
| name | num  |
+------+------+
| a    | 0001 |
| a    | 0002 |
| b    | 0001 |
| b    | 0002 |
| c    | 0001 |
| c    | 0002 |
| d    | 0001 |
| d    | 0002 |
+------+------+
8 rows in set (0.00 sec)

观察结果可知,查询一张有4条记录的表和一张有2条记录的表,最终结果有4×2=8条记录,这个过程就类似于求两个集合的笛卡尔积,这个结果除了给数据库造成压力,没有其他意义,应该尽量避免出现笛卡尔积

动态数据

FROM后面跟的数据不是一个实体表,而是一个从表中查询出来得到的二维结果表(子查询)

基本语法:FROM (SELECT 字段列表 FROM 表名) AS 别名;

注意这其中的括号和别名都是不能省略的

错误示例

1
2
3
4
mysql> SELECT * FROM SELECT * FROM my_simple;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT * FROM my_simple' at line 1
mysql> SELECT * FROM (SELECT * FROM my_simple);
ERROR 1248 (42000): Every derived table must have its own alias

正确示例

1
2
3
4
5
6
7
8
9
10
mysql> SELECT * FROM (SELECT * FROM my_simple) AS simple_my;
+------+
| name |
+------+
| a    |
| b    |
| c    |
| d    |
+------+
4 rows in set (0.00 sec)

从这我们也知道了as不仅能给字段做别名,也能给表做别名

Where子句

Where字句:用来从数据表获取数据的时候进行条件筛选

数据获取原理:针对表去对应的磁盘处获取所有的记录(一条条),where的作用就是在拿到一条结果就开始进行判断,判断是否符合条件,如果符合就保存下来,如果不符合直接舍弃(不放到内存中)

Where是通过运算符进行结果比较来判断数据

Group by子句

Group by表示分组的含义:根据指定的字段,将数据进行分组,目的是为了统计

分组统计

基本语法:GROUP BY 字段名;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+
| stu_id  | stu_name | class_id | stu_age | stu_height |
+---------+----------+----------+---------+------------+
| stu0001 | 夏洛     |        1 |      18 |        185 |
| stu0002 | 张四     |        1 |      28 |        165 |
| stu0003 | 张五     |        2 |      22 |        187 |
| stu0004 | 小婷     |        2 |      25 |        189 |
+---------+----------+----------+---------+------------+
4 rows in set (0.00 sec)
  
mysql> SELECT * FROM my_student GROUP BY class_id;
+---------+----------+----------+
| stu_id  | stu_name | class_id |
+---------+----------+----------+
| stu0001 | 夏洛     |        1 |
| stu0003 | 张五     |        2 |
+---------+----------+----------+
2 rows in set (0.03 sec)

group by是为了分组后进行数据统计的,group by将数据按照指定的字段分组之后,只会保留每组的第一条记录,如果只是想看数据显示,那么group by用处不大

那么怎么统计呢? 可以借助一些统计函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+
| stu_id  | stu_name | class_id | stu_age | stu_height |
+---------+----------+----------+---------+------------+
| stu0001 | 夏洛     |        1 |      18 |        185 |
| stu0002 | 张四     |        1 |      28 |        165 |
| stu0003 | 张五     |        2 |      22 |        187 |
| stu0004 | 小婷     |        2 |      25 |        189 |
+---------+----------+----------+---------+------------+
4 rows in set (0.00 sec)
 
mysql> -- 使用聚合函数:按照班级统计每班人数,最大年龄,最矮的身高,平均年龄
mysql> SELECT class_id,GROUP_CONCAT(stu_name),COUNT(*),MAX(stu_age),MIN(stu_height),AVG(stu_age) FROM my_student GROUP BY class_id;
+----------+------------------------+----------+--------------+-----------------+--------------+
| class_id | GROUP_CONCAT(stu_name) | COUNT(*) | MAX(stu_age) | MIN(stu_height) | AVG(stu_age) |
+----------+------------------------+----------+--------------+-----------------+--------------+
|        1 | 夏洛,张四              |        2 |           28 |             165 |      23.0000 |
|        2 | 张五,小婷              |        2 |           25 |             187 |      23.5000 |
+----------+------------------------+----------+--------------+-----------------+--------------+
2 rows in set (0.01 sec)

多分组

将数据按照某个字段进行分组之后,对已经分组的数据按照另一个字段进行再次分组

基本语法:GROUP BY 字段1,字段2,...;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
+---------+----------+----------+---------+------------+--------+
6 rows in set (0.00 sec)
 
mysql> SELECT class_id,gender,COUNT(*),GROUP_CONCAT(stu_name) FROM my_student GROUP BY class_id,gender;
+----------+--------+----------+------------------------+
| class_id | gender | COUNT(*) | GROUP_CONCAT(stu_name) |
+----------+--------+----------+------------------------+
|        1 | 男     |        1 | 夏洛                   |
|        1 | 女     |        2 | 张四,小猪              |
|        2 | 男     |        2 | 张五,小狗              |
|        2 | 女     |        1 | 小婷                   |
+----------+--------+----------+------------------------+
4 rows in set (0.00 sec)

可以理解为将所有学生按班级分为“1班”和“2班”两组,这两组再按性别各分为“1班男生”、“1班女生”和“2班男生”、“2班女生”

过程如下

回溯统计

我感觉回溯统计像是多分组的及逆统计过程

数据进行多分组之后,向上统计的过程中,需要进行层层上报,将这种层层上报统计的过程称之为回溯统计:每一次分组向上统计的过程都会产生一次新的统计数据,而且当前数据对应的分组字段为NULL

基本语法:GROUP BY 字段 WITH ROLLUP;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
+---------+----------+----------+---------+------------+--------+
6 rows in set (0.00 sec)
 
mysql> SELECT class_id,gender,COUNT(*),GROUP_CONCAT(stu_name) FROM my_student GROUP BY class_id,gender WITH ROLLUP;
+----------+--------+----------+-------------------------------------------+
| class_id | gender | COUNT(*) | GROUP_CONCAT(stu_name)                    |
+----------+--------+----------+-------------------------------------------+
|        1 | 男     |        1 | 夏洛                                      |
|        1 | 女     |        2 | 张四,小猪                                 |
|        1 | NULL   |        3 | 夏洛,张四,小猪                            |
|        2 | 男     |        2 | 张五,小狗                                 |
|        2 | 女     |        1 | 小婷                                      |
|        2 | NULL   |        3 | 张五,小狗,小婷                            |
|     NULL | NULL   |        6 | 夏洛,张四,小猪,张五,小狗,小婷             |
+----------+--------+----------+-------------------------------------------+
7 rows in set (0.00 sec)

多分组的过程是:数据源 -> 按班级分组 -> 按性别分组 -> 最终结果

而回溯统计的过程是:最终结果 -> 不同性别合并 -> 不同班级合并 -> 数据源

每一处合并都统计,按什么字段合并什么字段就变为NULL

Having子句

having的本质和where一样,是用来进行数据条件筛选

having的语法也与where类似

having是在group by子句之后,可以针对分组数据进行统计筛选,where不可以

比如说我要查询班级人数大于等于4的班级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
| stu0007 | 小江     |        1 |      25 |        178 | 女     |
+---------+----------+----------+---------+------------+--------+
7 rows in set (0.02 sec)
 
mysql> SELECT class_id,count(*) AS number FROM my_student WHERE count(*)>=4 GROUP BY class_id;
ERROR 1111 (HY000): Invalid use of group function

报错是因为where不能使用聚合函数,聚合函数是用在group by分组之后,此时where已经运行完毕

having在group by分组之后发挥作用,因此可以使用聚合函数或者字段别名(where是从磁盘的表中取出数据,别名是在数据进入到内存之后才有的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
| stu0007 | 小江     |        1 |      25 |        178 | 女     |
+---------+----------+----------+---------+------------+--------+
7 rows in set (0.00 sec)
 
mysql> SELECT class_id,count(*) AS number FROM my_student GROUP BY class_id HAVING number>=4;
+----------+--------+
| class_id | number |
+----------+--------+
|        1 |      4 |
+----------+--------+
1 row in set (0.00 sec)

强调:having是在group by之后,where是在group by之前,使用where的时候表示将数据从磁盘拿到内存,where之后的所有操作都是内存操作

Order by子句

Order by排序:根据校对规则对数据进行排序

基本语法:ORDER BY 字段 [ASC|DESC];

asc(默认选项):升序(Ascending)

desc:降序(Descending)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> -- 班级学生按照身高排序
mysql> SELECT * FROM my_student ORDER BY stu_height ASC;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0007 | 小江     |        1 |      25 |        178 | 女     |
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
+---------+----------+----------+---------+------------+--------+
7 rows in set (0.01 sec)

Order by可以进行多字段排序:先按照第一个字段进行排序,然后再按照第二个字段进行排序...

基本语法:ORDER BY 字段1 [ASC|DESC],字段2 [ASC|DESC];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> -- 按照班级以及身高排序
mysql> SELECT * FROM my_student ORDER BY class_id DESC,stu_height;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0007 | 小江     |        1 |      25 |        178 | 女     |
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
+---------+----------+----------+---------+------------+--------+
7 rows in set (0.00 sec)

Limit子句

Limit限制子句:主要是用来限制记录数量获取

记录数限制

纯粹的限制获取的数量,从第一条到指定的数量

基本语法:limit 数量;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
| stu0007 | 小江     |        1 |      25 |        178 | 女     |
+---------+----------+----------+---------+------------+--------+
7 rows in set (0.00 sec)
 
mysql> SELECT * FROM my_student LIMIT 2;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
+---------+----------+----------+---------+------------+--------+
2 rows in set (0.00 sec)

分页

利用limit来限制获取指定区间的数据

基本语法:LIMIT offset,length;

offset:偏移量,也就是说从哪开始
length:就是具体要获取多少条记录

注意:Mysql中记录的数量是从0开始的,例如LIMIT 0,2;表示获取前两条记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
mysql> SELECT * FROM my_student;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
| stu0005 | 小猪     |        1 |      30 |        173 | 女     |
| stu0006 | 小狗     |        2 |      18 |        170 | 男     |
| stu0007 | 小江     |        1 |      25 |        178 | 女     |
+---------+----------+----------+---------+------------+--------+
7 rows in set (0.05 sec)
 
mysql> SELECT * FROM my_student LIMIT 0,2;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0001 | 夏洛     |        1 |      18 |        185 | 男     |
| stu0002 | 张四     |        1 |      28 |        165 | 女     |
+---------+----------+----------+---------+------------+--------+
2 rows in set (0.00 sec)
 
mysql> SELECT * FROM my_student LIMIT 2,2;
+---------+----------+----------+---------+------------+--------+
| stu_id  | stu_name | class_id | stu_age | stu_height | gender |
+---------+----------+----------+---------+------------+--------+
| stu0003 | 张五     |        2 |      22 |        187 | 男     |
| stu0004 | 小婷     |        2 |      25 |        189 | 女     |
+---------+----------+----------+---------+------------+--------+
2 rows in set (0.00 sec)

注意:limit后面的length表示最多获取的数量,但是如果数量不够,系统并不会强求