黑马程序员 MySQL数据库入门到精通——进阶篇(2)
- 1. SQL优化
- 1.1 插入数据
- 1.2 主键优化
- 1.3 order by优化
- 1.4 group by优化
- 1.5 limit优化
- 1.6 count优化
- 1.7 update优化
- 2. 视图
- 2.1 视图-介绍及基本语法
- 2.2 视图-检查选项(cascaded)
- 2.3 视图-检查选项(local)
- 2.4 视图-更新及作用
- 2.5 视图-案例
- 3. 存储过程
- 3.1 存储过程介绍
- 3.2 存储过程基本语法
- 3.3 存储过程-变量系统变量
- 3.4 存储过程变量-用户定义变量
- 3.5 存储过程变量-局部变量
- 3.6 存储过程-if判断
- 3.7 存储过程- 参数(IN,OUT,INOUT)
- 3.8 存储过程-case
- 3.9 存储过程- 循环-while
- 3.10 存储过程循环-repeat
- 3.11 存储过程-循环-loop
- 3.12 存储过程游标-cursor
- 3.13 存储过程条件处理程序-handler
- 3.14 存储函数
- 4. 触发器
- 4.1 触发器-介绍
- 4.2 触发器-案例1(insert类型)
- 4.3 触发器-案例2(update类型)
- 4.4 触发器-案例3(delete类型)
- 4.5 视图&存储过程&触发器-小结
1. SQL优化
1.1 插入数据
insert into tb_ test values(1,'tom');
insert into tb_ test values(2,'cat');
insert into tb_ test values(3,'jerry');
.....
对于这样的数据我们想要插入多条数据,即批量插入应该怎么办?
优化方法:
insert into tb_ test values(1,'tom'),(2,'cat'),(3,'jerry');
500-1000条比较合适,再多的数据量就不太合适了;- 手动提交事务
start transaction;
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
insert into tb_test values(4,'Tom'),(5,'Cat'),(6,'Jerry');
insert into tb_test values(7,'Tom'),(8,'Cat'),(9,'Jerry');
commit;
-
主键顺序插入
主键乱序插入: 8 19 21 88 2 4 15 89 5 7 3
主键顺序插入: 1 2 3 4 5 7 8 9 15 21 88 89 -
大批量插入数据
如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入。操作如下:
1,jdTmmKQlwu1,jdTmmKQlwu,jdTmmKQlwu,2020-10-13,1
2,BTJOeWjRiw2,BTJOeWjRiw,BTJOeWjRiw,2020-6-12,2
3,waQTJIIlHI3,waQTJIIlHI,waQTJIIlHI,2020-6-2,0
4,XmeFHwozIo4,XmeFHwozIo,XmeFHwozIo,2020-1-11,1
5,xRrvQSHcZn5,xRrvQSHcZn,xRrvQSHcZn,2020-10-18,2
6,gTDfGFNLEj6,gTDfGFNLEj,gTDfGFNLEj,2020-1-13,0
7,nBETIlVCle7,nBETIlVCle,nBETIlVCle,2020-9-27,1
8,vmePKKZjJU8,vmePKKZjJU,vmePKKZjJU,2020-10-20,2
9,pWjaLhJVaB9,pWjaLhJVaB,pWjaLhJVaB,2020-5-7,0
10,zimgGFPEQe10,zimgGFPEQe,zimgGFPEQe,2020-8-1,1
11,uHpIsEALNp11,uHpIsEALNp,uHpIsEALNp,2020-12-17,2
12,kCfPeCgMjn12,kCfPeCgMjn,kCfPeCgMjn,2020-7-13,0
13,QRkLwosIdM13,QRkLwosIdM,QRkLwosIdM,2020-8-24,1
14,ipsKyeFeJy14,ipsKyeFeJy,ipsKyeFeJy,2020-2-22,2
15,ZRrcPvYDtF15,ZRrcPvYDtF,ZRrcPvYDtF,2020-12-7,0
16,BbLUYWHeTg16,BbLUYWHeTg,BbLUYWHeTg,2020-4-17,1
17,DalMVUpMnk17,DalMVUpMnk,DalMVUpMnk,2020-10-16,2
18,HFtmFtnZPi18,HFtmFtnZPi,HFtmFtnZPi,2020-11-25,0
19,maHoYocalj19,maHoYocalj,maHoYocalj,2020-3-3,1
20,nHKmkDGSeH20,nHKmkDGSeH,nHKmkDGSeH,2020-1-2,2
21,MZZziMtXLH21,MZZziMtXLH,MZZziMtXLH,2020-4-1,0
22,bjUXRogYgF22,bjUXRogYgF,bjUXRogYgF,2020-7-12,1
23,CVgMEIwyGf23,CVgMEIwyGf,CVgMEIwyGf,2020-12-5,2
24,yXVrVLgSmR24,yXVrVLgSmR,yXVrVLgSmR,2020-4-11,0
25,oaFXNzAigC25,oaFXNzAigC,oaFXNzAigC,2020-11-9,1
26,IJrJiutZtD26,IJrJiutZtD,IJrJiutZtD,2020-7-17,2
27,WGwcrfrFOB27,WGwcrfrFOB,WGwcrfrFOB,2020-9-22,0
28,RbCMhegoiU28,RbCMhegoiU,RbCMhegoiU,2020-6-1,1
29,RzRzNPEsQm29,RzRzNPEsQm,RzRzNPEsQm,2020-2-24,2
30,SYzgGoVRwv30,SYzgGoVRwv,SYzgGoVRwv,2020-7-3,0
31,hLuUHxjJhk31,hLuUHxjJhk,hLuUHxjJhk,2020-1-9,1
32,jhUhcVaQkV32,jhUhcVaQkV,jhUhcVaQkV,2020-4-27,2
33,MmbKbOrEpK33,MmbKbOrEpK,MmbKbOrEpK,2020-5-2,0
34,tdEHJZKNBh34,tdEHJZKNBh,tdEHJZKNBh,2020-1-1,1
35,ydXuLXfCdl35,ydXuLXfCdl,ydXuLXfCdl,2020-7-6,2
36,NmFBcmZeAa36,NmFBcmZeAa,NmFBcmZeAa,2020-3-13,0
37,MRTcthPOWV37,MRTcthPOWV,MRTcthPOWV,2020-5-14,1
38,VOXAUgckhi38,VOXAUgckhi,VOXAUgckhi,2020-4-5,2
39,MRmlUaVmKk39,MRmlUaVmKk,MRmlUaVmKk,2020-1-16,0
40,wZWYFhLRpr40,wZWYFhLRpr,wZWYFhLRpr,2020-4-2,1
41,LMrYOftcSY41,LMrYOftcSY,LMrYOftcSY,2020-11-7,2
42,CdragbnGZi42,CdragbnGZi,CdragbnGZi,2020-5-14,0
43,UoxlSsMmRn43,UoxlSsMmRn,UoxlSsMmRn,2020-4-1,1
44,awmXaRMqwr44,awmXaRMqwr,awmXaRMqwr,2020-12-23,2
45,IeDxjlQzeC45,IeDxjlQzeC,IeDxjlQzeC,2020-5-1,0
46,NHNMuLsSrc46,NHNMuLsSrc,NHNMuLsSrc,2020-1-20,1
47,IUHRbNbOzz47,IUHRbNbOzz,IUHRbNbOzz,2020-12-12,2
48,qpcNRHPdEP48,qpcNRHPdEP,qpcNRHPdEP,2020-1-26,0
49,ziHUyxnecB49,ziHUyxnecB,ziHUyxnecB,2020-2-5,1
50,XlcJSJbXuF50,XlcJSJbXuF,XlcJSJbXuF,2020-1-10,2
#客户端连接服务端时,加.上参数--local-infile
mysql --local -infile -u root -p
#设置全局参数local infile为1, 开启从本地加载文件导入数据的开关
set global local_infile= 1;
#执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table 'tb_user' fields terminated by ',' lines terminated by \n';
$ mysql --local-infile -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 34
Server version: 8.0.34-0ubuntu0.20.04.1 (Ubuntu)Copyright (c) 2000, 2023, Oracle and/or its affiliates.Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| tb_user |
+--------------------+
5 rows in set (0.09 sec)mysql> create database itheima;
Query OK, 1 row affected (0.04 sec)mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| itheima |
| mysql |
| performance_schema |
| sys |
| tb_user |
+--------------------+
6 rows in set (0.01 sec)mysql> select @@local_infile = 1;
+--------------------+
| @@local_infile = 1 |
+--------------------+
| 0 |
+--------------------+
1 row in set (0.00 sec)
建立
mysql> set global local_infile =1;
Query OK, 0 rows affected (0.00 sec)mysql> select @@local_infile = 1;
+--------------------+
| @@local_infile = 1 |
+--------------------+
| 1 |
+--------------------+
1 row in set (0.00 sec)mysql> use itheima;
Database changed
mysql> CREATE TABLE `tb_user` (-> `id` INT(11) NOT NULL AUTO_INCREMENT,-> `username` VARCHAR(50) NOT NULL,-> `password` VARCHAR(50) NOT NULL,-> `name` VARCHAR(20) NOT NULL,-> `birthday` DATE DEFAULT NULL,-> `sex` CHAR(1) DEFAULT NULL,-> PRIMARY KEY (`id`),-> UNIQUE KEY `unique_user_username` (`username`)-> ) ENGINE=INNODB DEFAULT CHARSET=utf8 ;
Query OK, 0 rows affected, 2 warnings (0.21 sec)mysql>
mysql> load data local infile '/home/matthew/文档/load_user_100w_sql.sql' into table tb_user fields terminated by ',' lines terminated by '\n';
Query OK, 50 rows affected (0.21 sec)
Records: 50 Deleted: 0 Skipped: 0 Warnings: 0
1.2 主键优化
在上一小节中,我们说主键的顺序插入的性能是高于乱序插入的。为什么???
我们先了解一下:
数据组织方式
🐬🐬🐬在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table IOT)。
页分裂
🐬页可以为空、也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据多大,会行溢出),根据主键排列。
顺序插入的话:
但是要是乱序插入!!!
以上过程,找到50%的地方,然后23 47移动到第三个页 然后重新定义指针顺序——页分裂
那么与之相对应的就是页合并
要是删除的时候,
🐬当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged) 为删除并且它的空间变得允许被其他记录声明使用。
🐬当页中删除的记录达到MERGE_THRESHOLD ( 默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
知识小贴士:MERGE_THRESHOLD合并页的阈值,可以自己设置,在创建表或者创建索引时指定。
🐬主键设计原则
- 满足业务需求的情况下,尽量降低主键的长度。
- 插入数据时,尽量选择顺序插入,选择使用AUTO_ INCREMENT自增主键。
- 尽量不要使用UUID做主键或者是其他自然主键,如身份证号。
- 业务操作时,避免对主键的修改。
1.3 order by优化
- Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序。
- Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高。
mysql> use tb_user;
Database changed
mysql> select * from tb_user;
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| id | name | phone | email | profession | age | gender | status | createtime |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| 1 | 吕布 | 17799990000 | lvbu666@163.com | 软件工程 | 23 | 1 | 6 | 2001-02-02 00:00:00 |
| 2 | 曹操 | 17799990001 | caocao666@qq.com | 通讯工程 | 33 | 1 | 0 | 2001-03-05 00:00:00 |
| 3 | 赵云 | 17799990002 | 17799990@139.com | 英语 | 34 | 1 | 2 | 2002-03-02 00:00:00 |
| 4 | 孙悟空 | 17799990003 | 17799990@sina.com | 工程造价 | 54 | 1 | 0 | 2001-07-02 00:00:00 |
| 5 | 花木兰 | 17799990004 | 19980729@sina.com | 软件工程 | 23 | 2 | 1 | 2001-04-22 00:00:00 |
| 6 | 大乔 | 17799990005 | daqiao666@sina.com | 舞蹈 | 22 | 2 | 0 | 2001-02-07 00:00:00 |
| 7 | 露娜 | 17799990006 | luna_love@sina.com | 应用数学 | 24 | 2 | 0 | 2001-02-08 00:00:00 |
| 8 | 程咬金 | 17799990007 | chengyaojin@163.com | 化工 | 38 | 1 | 5 | 2001-05-23 00:00:00 |
| 9 | 项羽 | 17799990008 | xiaoyu666@qq.com | 金属材料 | 43 | 1 | 0 | 2001-09-18 00:00:00 |
| 10 | 白起 | 17799990009 | baiqi666@sina.com | 机械工程及其自动化 | 27 | 1 | 2 | 2001-08-16 00:00:00 |
| 11 | 韩信 | 17799990010 | hanxin520@163.com | 无机非金属材料工程 | 27 | 1 | 0 | 2001-06-12 00:00:00 |
| 12 | 荆轲 | 17799990011 | jingke123@163.com | 会计 | 29 | 1 | 0 | 2001-05-11 00:00:00 |
| 13 | 兰陵王 | 17799990012 | lanlinwang666@126.com | 工程造价 | 44 | 1 | 1 | 2001-04-09 00:00:00 |
| 14 | 狂铁 | 17799990013 | kuangtie@sina.com | 应用数学 | 43 | 1 | 2 | 2001-04-10 00:00:00 |
| 15 | 貂蝉 | 17799990014 | 84958948374@qq.com | 软件工程 | 40 | 2 | 3 | 2001-02-12 00:00:00 |
| 16 | 妲己 | 17799990015 | 2783238293@qq.com | 软件工程 | 31 | 2 | 0 | 2001-01-30 00:00:00 |
| 17 | 芈月 | 17799990016 | xiaomin2001@sina.com | 工业经济 | 35 | 2 | 0 | 2000-05-03 00:00:00 |
| 18 | 嬴政 | 17799990017 | 8839434342@qq.com | 化工 | 38 | 1 | 1 | 2001-08-08 00:00:00 |
| 19 | 狄仁杰 | 17799990018 | jujiamlm8166@163.com | 国际贸易 | 30 | 1 | 0 | 2007-03-12 00:00:00 |
| 20 | 安琪拉 | 17799990019 | jdodm1h@126.com | 城市规划 | 51 | 2 | 0 | 2001-08-15 00:00:00 |
| 21 | 典韦 | 17799990020 | ycaunanjian@163.com | 城市规划 | 52 | 1 | 2 | 2000-04-12 00:00:00 |
| 22 | 廉颇 | 17799990021 | lianpo321@126.com | 土木工程 | 19 | 1 | 3 | 2002-07-18 00:00:00 |
| 23 | 后羿 | 17799990022 | altycj2000@139.com | 城市园林 | 20 | 1 | 0 | 2002-03-10 00:00:00 |
| 24 | 姜子牙 | 17799990023 | 37483844@qq.com | 工程造价 | 29 | 1 | 4 | 2003-05-26 00:00:00 |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
24 rows in set (0.00 sec)mysql> show index from tb_userl-> ;
ERROR 1146 (42S02): Table 'tb_user.tb_userl' doesn't exist
mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 0 | idx_user_phone | 1 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_name | 1 | name | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_email_5 | 1 | email | A | 23 | 5 | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
7 rows in set (0.21 sec)mysql> drop index idx_user_phone on tb_user;
Query OK, 0 rows affected (0.21 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> drop index idx_user_name on tb_user;
Query OK, 0 rows affected (0.09 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_email_5 | 1 | email | A | 23 | 5 | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
5 rows in set (0.00 sec)
mysql> select id,age,phone from tb_user order by age;
+----+------+-------------+
| id | age | phone |
+----+------+-------------+
| 22 | 19 | 17799990021 |
| 23 | 20 | 17799990022 |
| 6 | 22 | 17799990005 |
| 1 | 23 | 17799990000 |
| 5 | 23 | 17799990004 |
| 7 | 24 | 17799990006 |
| 10 | 27 | 17799990009 |
| 11 | 27 | 17799990010 |
| 24 | 29 | 17799990023 |
| 12 | 29 | 17799990011 |
| 19 | 30 | 17799990018 |
| 16 | 31 | 17799990015 |
| 2 | 33 | 17799990001 |
| 3 | 34 | 17799990002 |
| 17 | 35 | 17799990016 |
| 18 | 38 | 17799990017 |
| 8 | 38 | 17799990007 |
| 15 | 40 | 17799990014 |
| 14 | 43 | 17799990013 |
| 9 | 43 | 17799990008 |
| 13 | 44 | 17799990012 |
| 20 | 51 | 17799990019 |
| 21 | 52 | 17799990020 |
| 4 | 54 | 17799990003 |
+----+------+-------------+
24 rows in set (0.01 sec)
走的是全表扫描!!!!
mysql> explain select id,age,phone from tb_user order by age;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | tb_user | NULL | ALL | NULL | NULL | NULL | NULL | 24 | 100.00 | Using filesort |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select id,age,phone from tb_user order by age,phone;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | tb_user | NULL | ALL | NULL | NULL | NULL | NULL | 24 | 100.00 | Using filesort |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
#没有创建索引时,根据age, phone进行排序
explain select id,age,phone from tb_user order by age,phone;
#创建索引
create index idx_user_age_phone_aa on tb_user(age,phone);
#创建索引后,根据age, phone进行升序排序
explain select id,age,phone from tb_user order by age,phone;
#创建索引后,根据age, phone进行降序排序
explain select id,age,phone from tb_user order by age desc, phone desc ;
mysql> explain select id,age,phone from tb_user order by age,phone;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | tb_user | NULL | ALL | NULL | NULL | NULL | NULL | 24 | 100.00 | Using filesort |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.01 sec)mysql> create index idx_user_age_phone_aa on tb_user(age,phone);
Query OK, 0 rows affected (0.64 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> explain select id,age,phone from tb_user order by age,phone;
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | index | NULL | idx_user_age_phone_aa | 48 | NULL | 24 | 100.00 | Using index |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)mysql> explain select id,age,phone from tb_user order by age desc, phone desc ;
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+----------------------------------+
| 1 | SIMPLE | tb_user | NULL | index | NULL | idx_user_age_phone_aa | 48 | NULL | 24 | 100.00 | Backward index scan; Using index |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)
观察下面
mysql> explain select id,age,phone from tb_user order by phone,age ;
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | tb_user | NULL | index | NULL | idx_user_age_phone_aa | 48 | NULL | 24 | 100.00 | Using index; Using filesort |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)
违背了最左前缀法则,所以会出现Using filesort
如果是下面代码呢??
mysql> explain select id,age,phone from tb_user order by age asc, phone desc ;
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | tb_user | NULL | index | NULL | idx_user_age_phone_aa | 48 | NULL | 24 | 100.00 | Using index; Using filesort |
+----+-------------+---------+------------+-------+---------------+-----------------------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)
正常来讲是按照正序拍的,但是书写了 desc,所以要多余的排列,所以会出现Using filesort
那么我想倒着排列怎么办!!!创建索引特殊说明一下即可:
mysql> create index idx_age_pho_ad on tb_user(age asc,phone desc);
Query OK, 0 rows affected (0.16 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> show index from tb_user;
+---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_email_5 | 1 | email | A | 23 | 5 | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_age_phone_aa | 1 | age | A | 19 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_age_phone_aa | 2 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_age_pho_ad | 1 | age | A | 19 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_age_pho_ad | 2 | phone | D | 24 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
9 rows in set (0.18 sec)
🐬这样就不需要用出现Using filesort
mysql> explain select id,age,phone from tb_user order by age asc, phone desc ;
+----+-------------+---------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | index | NULL | idx_age_pho_ad | 48 | NULL | 24 | 100.00 | Using index |
+----+-------------+---------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
1.4 group by优化
#删除掉目前的联合索idx_user_pro_age_sta;
drop index idx_user_pro_age_sta on tb_user;
#执行分组操作,根据profession字段分组
explain select profession,count(*) from tb_user group by profession;
#创建索引
Create index idx_user_pro_age_sta on tb_user(profession, age,status);
#执行分组操作,根据profession字段分组
explain select profession , count(*) from tb_user group by profession;
#执行分组操作,根据age字段分组
explain select profession , count(*) from tb_user group by age;
不满足最左前缀准准
#执行分组操作,根据profession字 段分组
explain select profession , count(*) from tb_user group by profession, age;
mysql> show index from tb_user;
+---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_email_5 | 1 | email | A | 23 | 5 | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_age_phone_aa | 1 | age | A | 19 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_age_phone_aa | 2 | phone | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_age_pho_ad | 1 | age | A | 19 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_age_pho_ad | 2 | phone | D | 24 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
6 rows in set (0.00 sec)mysql> drop index idx_email_5 on tb_user;
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> drop index idx_user_age_phone_aa on tb_user;
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> drop index idx_age_pho_ad on tb_user;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> show index from tb_user;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.01 sec)
mysql> select profession,count(*) from tb_user group by profession;
+-----------------------------+----------+
| profession | count(*) |
+-----------------------------+----------+
| 软件工程 | 4 |
| 通讯工程 | 1 |
| 英语 | 1 |
| 工程造价 | 3 |
| 舞蹈 | 1 |
| 应用数学 | 2 |
| 化工 | 2 |
| 金属材料 | 1 |
| 机械工程及其自动化 | 1 |
| 无机非金属材料工程 | 1 |
| 会计 | 1 |
| 工业经济 | 1 |
| 国际贸易 | 1 |
| 城市规划 | 2 |
| 土木工程 | 1 |
| 城市园林 | 1 |
+-----------------------------+----------+
16 rows in set (0.04 sec)
mysql> explain select profession,count(*) from tb_user group by profession;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | SIMPLE | tb_user | NULL | ALL | NULL | NULL | NULL | NULL | 24 | 100.00 | Using temporary |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------+
1 row in set, 1 warning (0.00 sec)
mysql> Create index idx_user_pro_age_sta on tb_user(profession, age,status);
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user | 0 | PRIMARY | 1 | id | A | 24 | NULL | NULL | | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 1 | profession | A | 16 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 2 | age | A | 22 | NULL | NULL | YES | BTREE | | | YES | NULL |
| tb_user | 1 | idx_user_pro_age_sta | 3 | status | A | 24 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
4 rows in set (0.00 sec)mysql> explain select profession,count(*) from tb_user group by profession;
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | index | idx_user_pro_age_sta | idx_user_pro_age_sta | 54 | NULL | 24 | 100.00 | Using index |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select profession , count(*) from tb_user group by profession, age;
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | index | idx_user_pro_age_sta | idx_user_pro_age_sta | 54 | NULL | 24 | 100.00 | Using index |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
1.5 limit优化
🐬对于大数据表的时候,1千万,执行分页操作的话!
mysql> select * from tb_user limit 0,10;
+----+-----------+-------------+---------------------+-----------------------------+------+--------+--------+---------------------+
| id | name | phone | email | profession | age | gender | status | createtime |
+----+-----------+-------------+---------------------+-----------------------------+------+--------+--------+---------------------+
| 1 | 吕布 | 17799990000 | lvbu666@163.com | 软件工程 | 23 | 1 | 6 | 2001-02-02 00:00:00 |
| 2 | 曹操 | 17799990001 | caocao666@qq.com | 通讯工程 | 33 | 1 | 0 | 2001-03-05 00:00:00 |
| 3 | 赵云 | 17799990002 | 17799990@139.com | 英语 | 34 | 1 | 2 | 2002-03-02 00:00:00 |
| 4 | 孙悟空 | 17799990003 | 17799990@sina.com | 工程造价 | 54 | 1 | 0 | 2001-07-02 00:00:00 |
| 5 | 花木兰 | 17799990004 | 19980729@sina.com | 软件工程 | 23 | 2 | 1 | 2001-04-22 00:00:00 |
| 6 | 大乔 | 17799990005 | daqiao666@sina.com | 舞蹈 | 22 | 2 | 0 | 2001-02-07 00:00:00 |
| 7 | 露娜 | 17799990006 | luna_love@sina.com | 应用数学 | 24 | 2 | 0 | 2001-02-08 00:00:00 |
| 8 | 程咬金 | 17799990007 | chengyaojin@163.com | 化工 | 38 | 1 | 5 | 2001-05-23 00:00:00 |
| 9 | 项羽 | 17799990008 | xiaoyu666@qq.com | 金属材料 | 43 | 1 | 0 | 2001-09-18 00:00:00 |
| 10 | 白起 | 17799990009 | baiqi666@sina.com | 机械工程及其自动化 | 27 | 1 | 2 | 2001-08-16 00:00:00 |
+----+-----------+-------------+---------------------+-----------------------------+------+--------+--------+---------------------+
10 rows in set (0.00 sec)mysql> select * from tb_user limit 10,10;
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| id | name | phone | email | profession | age | gender | status | createtime |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
| 11 | 韩信 | 17799990010 | hanxin520@163.com | 无机非金属材料工程 | 27 | 1 | 0 | 2001-06-12 00:00:00 |
| 12 | 荆轲 | 17799990011 | jingke123@163.com | 会计 | 29 | 1 | 0 | 2001-05-11 00:00:00 |
| 13 | 兰陵王 | 17799990012 | lanlinwang666@126.com | 工程造价 | 44 | 1 | 1 | 2001-04-09 00:00:00 |
| 14 | 狂铁 | 17799990013 | kuangtie@sina.com | 应用数学 | 43 | 1 | 2 | 2001-04-10 00:00:00 |
| 15 | 貂蝉 | 17799990014 | 84958948374@qq.com | 软件工程 | 40 | 2 | 3 | 2001-02-12 00:00:00 |
| 16 | 妲己 | 17799990015 | 2783238293@qq.com | 软件工程 | 31 | 2 | 0 | 2001-01-30 00:00:00 |
| 17 | 芈月 | 17799990016 | xiaomin2001@sina.com | 工业经济 | 35 | 2 | 0 | 2000-05-03 00:00:00 |
| 18 | 嬴政 | 17799990017 | 8839434342@qq.com | 化工 | 38 | 1 | 1 | 2001-08-08 00:00:00 |
| 19 | 狄仁杰 | 17799990018 | jujiamlm8166@163.com | 国际贸易 | 30 | 1 | 0 | 2007-03-12 00:00:00 |
| 20 | 安琪拉 | 17799990019 | jdodm1h@126.com | 城市规划 | 51 | 2 | 0 | 2001-08-15 00:00:00 |
+----+-----------+-------------+-----------------------+-----------------------------+------+--------+--------+---------------------+
10 rows in set (0.00 sec)mysql>
🐬但是对于1千万的数据时,我要是使用select * from tb_sku limit 9000000,10;
,你就等吧!!
越往后时间越长!!!
- 一个常见又非常头疼的问题就是limit 2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000 - 2000010的记录,其他记录丢弃,查询排序的代价非常大。
- 所以!!!!
select s.* from tb_sku s, (select id from tb_sku order by id limit 9000000,10) a where s.id = a.id;
1.6 count优化
🐬 是由存储引擎决定的
🐬对于数据量非常大的表结构的时候。使用count
也是非常耗时的select count(*) from tb_sku;
🐬MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)
的时候会直接返回这个数,效率很高;当然前提是没有where
条件
🐬InnoDB引擎就麻烦了,它执行count(*)
的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。
优化思路:自己计数
- count()是一个聚合函数,对于返回的结果集,一行行地判断,如果count函数的参数不是NULL,累计值就加1,否则不加,最后返回累计值。
- 用法:
count (*)、count (主键)、count (字段)、count(1)
🐬🐬🐬count的几种用法
- count (主键)
- InnoDB引擎会遍历整张表,把每一行的 主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)。
- count (字段)
- 没有not null约束: InnoDB引擎会遍历整张表把每一行的字 段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。
- 有not null约束: InnoDB 引擎会遍历整张表把每一行的字段值都取出来, 返回给服务层,直接按行进行累加。
- count (1)
- InnoDB引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1” 进去,直接按行进行累加。
- count (*)
- InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。
🐬按照效率排序的话,count(字段)
< count(主键id)
< count(1)
≈ count(*)
,所以尽量使用count(*)
。
1.7 update优化
show tables;
select * from course;
update course set name = 'javaEE' where id =1;
更新数据时一定要根据索引字段,要不然不上行锁,而是表锁
update student set no =' 2000100100' where id = 1 ;
update student set no = '2000100105' where name='韦一笑';
🐬InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。
2. 视图
2.1 视图-介绍及基本语法
介绍
🐬视图(View) 是一种虛拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
🐬通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
准备工作
drop table if exists student;create table student(id int auto_increment comment '主键ID' primary key,name varchar(10) null comment '姓名',no varchar(10) null comment '学号'
)comment '学生表';INSERT INTO student (name, no) VALUES ('黛绮丝', '2000100101');
INSERT INTO student (name, no) VALUES ('谢逊', '2000100102');
INSERT INTO student (name, no) VALUES ('殷天正', '2000100103');
INSERT INTO student (name, no) VALUES ('韦一笑', '2000100104');create table course(id int auto_increment comment '主键ID' primary key,name varchar(10) null comment '课程名称'
)comment '课程表';INSERT INTO course (name) VALUES ('Java');
INSERT INTO course (name) VALUES ('PHP');
INSERT INTO course (name) VALUES ('MySQL');
INSERT INTO course (name) VALUES ('Hadoop');create table student_course(id int auto_increment comment '主键' primary key,studentid int not null comment '学生ID',courseid int not null comment '课程ID',constraint fk_courseid foreign key (courseid) references course (id),constraint fk_studentid foreign key (studentid) references student (id)
)comment '学生课程中间表';INSERT INTO student_course (studentid, courseid) VALUES (1, 1);
INSERT INTO student_course (studentid, courseid) VALUES (1, 2);
INSERT INTO student_course (studentid, courseid) VALUES (1, 3);
INSERT INTO student_course (studentid, courseid) VALUES (2, 2);
INSERT INTO student_course (studentid, courseid) VALUES (2, 3);
INSERT INTO student_course (studentid, courseid) VALUES (3, 4);
创建视图
CREATE [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT语句 [WITH[CASCADED | LOCAL] CHECK OPTION ]
查询视图
查看创建视图语句: SHOW CREATE VIEW 视图名称;
查看视图数据: SELECT * FROM 视图名称.....;
修改视图
方式一: CREATE [OR REPLACE] VIEW 视图名称[(列名列表)] AS SELECT语句 [WITH[CASCADED| LOCAL] CHECK OPTION]
方式二: ALTER VIEW 视图名称[(列名列表)] AS SELECT语句 [WITH[CASCADED | LOCAL] CHECK OPTION]
删除视图
DROP VIEW [F EXISTS]视图名称[视图名称] ...
-- 创建视图
create or replace view stu_v_1 as select id,name from student where id <= 10;-- 查询视图
show create view stu_v_1;select * from stu_v_1;select * from stu_v_1 where id < 3;-- 修改视图
create or replace view stu_v_1 as select id,name,no from student where id <= 10;select * from stu_v_1;alter view stu_v_1 as select id,name from student where id <= 10;select * from stu_v_1;-- 删除视图
drop view if exists stu_v_1;
2.2 视图-检查选项(cascaded)
create or replace view stu_v_1 as select id,name from student where id <= 10 with cascaded check option ;select * from stu_v_1;insert into stu_v_1 values(6,'Tom');
insert into stu_v_1 values(30,'Tom12');
insert into stu_v_1 values(17,'Tom22');
添加检查语句的时候with cascaded check option
,(30,Tom)这句话运行报错!!!!
视图的检查选项
- 当使用WITH CHECK OPTION子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定义。
- MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。 为了确定检查的范围,mysql提供了两个选项:
🐬🐬🐬CASCADED和LOCAL,默认值为CASCADED。
CASCADED不仅要看你当前条件还要看你有所依赖的条件
-- cascaded
create or replace view stu_v_1 as select id,name from student where id <= 15 ;
-- 可以
insert into stu_v_1 values(5,'Tom');
-- 可以
insert into stu_v_1 values(16,'Tom');create or replace view stu_v_2 as select id,name from stu_v_1 where id >= 10 with cascaded check option ;
-- 可以
insert into stu_v_2 values(13,'Tom');
-- 可以
insert into stu_v_2 values(17,'Tom');create or replace view stu_v_3 as select id,name from stu_v_2 where id < 20 ;
-- 可以
insert into stu_v_3 values(14,'Tom');
2.3 视图-检查选项(local)
-- local
create or replace view stu_v_4 as select id,name from student where id <= 15 with local check option;-- 可以
insert into stu_v_4 values(5,'Tom');-- 报错
insert into stu_v_4 values(16,'Tom');create or replace view stu_v_5 as select id,name from stu_v_4 where id >= 10 with local check option ;
-- 可以
insert into stu_v_5 values(13,'Tom');
-- 报错
insert into stu_v_5 values(17,'Tom');
-- 报错
insert into stu_v_5 values(18,'Tom');create or replace view stu_v_6 as select id,name from stu_v_5 where id < 20 ;
-- 可以
insert into stu_v_6 values(14,'Tom');
2.4 视图-更新及作用
视图的更新
要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一项,则该视图不可更新:
- 聚合函数或窗口函数(SUM()、MIN()、 MAX()、 COUNT()等)
- DISTINCT
- GROUP BY
- HAVING
- UNION或者UNION ALL
-- 创建视图, 使用聚合函数
create view stu_v_count as select count(*) from student;
报错!!!!
insert into stu_v_count values(10);
作用
- 简单
- 视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。
- 安全
- 数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
- 数据独立
- 视图可帮助用户屏蔽真实表结构变化带来的影响。
2.5 视图-案例
- 为了保证数据库表的安全性。开发人员在操作tb_user表时,只能看到的用户的基本字段,屏蔽手机号和邮箱两个字段。
- 查询每个学生所选修的课程 (三张表联查) , 这个功能在很多的业务中都有使用到,为了简化操作,定义一个视图。
create table tb_user(id int primary key auto_increment comment '主键',name varchar(50) not null comment '用户名',phone varchar(11) not null comment '手机号',email varchar(100) comment '邮箱',profession varchar(11) comment '专业',age tinyint unsigned comment '年龄',gender char(1) comment '性别 , 1: 男, 2: 女',status char(1) comment '状态',createtime datetime comment '创建时间'
) comment '系统用户表';INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('吕布', '17799990000', 'lvbu666@163.com', '软件工程', 23, '1', '6', '2001-02-02 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('曹操', '17799990001', 'caocao666@qq.com', '通讯工程', 33, '1', '0', '2001-03-05 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('赵云', '17799990002', '17799990@139.com', '英语', 34, '1', '2', '2002-03-02 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('孙悟空', '17799990003', '17799990@sina.com', '工程造价', 54, '1', '0', '2001-07-02 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('花木兰', '17799990004', '19980729@sina.com', '软件工程', 23, '2', '1', '2001-04-22 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('大乔', '17799990005', 'daqiao666@sina.com', '舞蹈', 22, '2', '0', '2001-02-07 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('露娜', '17799990006', 'luna_love@sina.com', '应用数学', 24, '2', '0', '2001-02-08 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('程咬金', '17799990007', 'chengyaojin@163.com', '化工', 38, '1', '5', '2001-05-23 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('项羽', '17799990008', 'xiaoyu666@qq.com', '金属材料', 43, '1', '0', '2001-09-18 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('白起', '17799990009', 'baiqi666@sina.com', '机械工程及其自动化', 27, '1', '2', '2001-08-16 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('韩信', '17799990010', 'hanxin520@163.com', '无机非金属材料工程', 27, '1', '0', '2001-06-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('荆轲', '17799990011', 'jingke123@163.com', '会计', 29, '1', '0', '2001-05-11 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('兰陵王', '17799990012', 'lanlinwang666@126.com', '工程造价', 44, '1', '1', '2001-04-09 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狂铁', '17799990013', 'kuangtie@sina.com', '应用数学', 43, '1', '2', '2001-04-10 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('貂蝉', '17799990014', '84958948374@qq.com', '软件工程', 40, '2', '3', '2001-02-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('妲己', '17799990015', '2783238293@qq.com', '软件工程', 31, '2', '0', '2001-01-30 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('芈月', '17799990016', 'xiaomin2001@sina.com', '工业经济', 35, '2', '0', '2000-05-03 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('嬴政', '17799990017', '8839434342@qq.com', '化工', 38, '1', '1', '2001-08-08 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狄仁杰', '17799990018', 'jujiamlm8166@163.com', '国际贸易', 30, '1', '0', '2007-03-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('安琪拉', '17799990019', 'jdodm1h@126.com', '城市规划', 51, '2', '0', '2001-08-15 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('典韦', '17799990020', 'ycaunanjian@163.com', '城市规划', 52, '1', '2', '2000-04-12 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('廉颇', '17799990021', 'lianpo321@126.com', '土木工程', 19, '1', '3', '2002-07-18 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('后羿', '17799990022', 'altycj2000@139.com', '城市园林', 20, '1', '0', '2002-03-10 00:00:00');
INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('姜子牙', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');
create table student(id int auto_increment comment '主键ID' primary key,name varchar(10) null comment '姓名',no varchar(10) null comment '学号'
)comment '学生表';INSERT INTO student (name, no) VALUES ('黛绮丝', '2000100101');
INSERT INTO student (name, no) VALUES ('谢逊', '2000100102');
INSERT INTO student (name, no) VALUES ('殷天正', '2000100103');
INSERT INTO student (name, no) VALUES ('韦一笑', '2000100104');create table course(id int auto_increment comment '主键ID' primary key,name varchar(10) null comment '课程名称'
)comment '课程表';INSERT INTO course (name) VALUES ('Java');
INSERT INTO course (name) VALUES ('PHP');
INSERT INTO course (name) VALUES ('MySQL');
INSERT INTO course (name) VALUES ('Hadoop');create table student_course(id int auto_increment comment '主键' primary key,studentid int not null comment '学生ID',courseid int not null comment '课程ID',constraint fk_courseid foreign key (courseid) references course (id),constraint fk_studentid foreign key (studentid) references student (id)
)comment '学生课程中间表';INSERT INTO student_course (studentid, courseid) VALUES (1, 1);
INSERT INTO student_course (studentid, courseid) VALUES (1, 2);
INSERT INTO student_course (studentid, courseid) VALUES (1, 3);
INSERT INTO student_course (studentid, courseid) VALUES (2, 2);
INSERT INTO student_course (studentid, courseid) VALUES (2, 3);
INSERT INTO student_course (studentid, courseid) VALUES (3, 4);
-- 案例 :
-- 1. 为了保证数据库表的安全性,开发人员在操作tb_user表时,只能看到的用户的基本字段,屏蔽手机号和邮箱两个字段。
create view tb_user_view as select id,name,profession,age,gender,status,createtime from tb_user;select * from tb_user_view;-- 2. 查询每个学生所选修的课程(三张表联查),这个功能在很多的业务中都有使用到,为了简化操作,定义一个视图。create view tb_stu_course_view as select s.name student_name , s.no student_no , c.name course_name from student s, student_course sc , course c where s.id = sc.studentid and sc.courseid = c.id;select * from tb_stu_course_view;
3. 存储过程
3.1 存储过程介绍
操作多次数据库就会涉及到多次网络请求,那怎么办啊??????
🐬可以把这些业务量逻辑进行封装!如图 封装成P1之后!我们的应用程序只需要调用P1即可
介绍
🐬存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。
🐬存储过程思想上很简单,就是数据库SQL语言层面的代码封装与重用。
特点
- 封装,复用
- 可以接收参数,也可以返回数据
- 减少(应用服务器与数据库)网络交互,效率提升
3.2 存储过程基本语法
下面介绍基础语法先用无参的
-- 存储过程基本语法
-- 创建
create procedure p1()
beginselect count(*) from student;
end;
-- 调用
call p1();
-- 查看
select * from information_schema.ROUTINES where ROUTINE_SCHEMA = 'itcast';show create procedure p1;-- 删除
drop procedure if exists p1;
但是在命令行中运行的时候,可能会出现一些问题,因为命令行在遇到分号的时候,就认为我写的这条语句结束了!!!
🐬注意:在命令行中。执行创建存储过程的SQL时,需要通过关键字delimiter指定SQL语句的结束符。
delimiter $$
-- 存储过程基本语法
-- 创建
create procedure p1()
beginselect count(*) from student;
end$$
不想用的时候,改回来就好了
delimiter ;
3.3 存储过程-变量系统变量
变量有三种🐬🐬🐬:
- 系统变量
- 用户定义变量
- 局部变量
系统变量是MySQL服务器提供,不是用户定义的,属于服务器层面。分为
- 全局变量(GLOBAL)
- 会话变量(SESSION)
-- 变量: 系统变量
-- 查看系统变量
show session variables ;show session variables like 'auto%';
show global variables like 'auto%';select @@global.autocommit;
select @@session.autocommit;-- 设置系统变量set global autocommit = 0;
insert into course(id, name) VALUES (6, 'ES');
commit;set session autocommit = 1;
select @@global.autocommit;
虽然设置的全局的set global autocommit = 0;
,但是你的服务器重启之后,还是默认值!!!!
3.4 存储过程变量-用户定义变量
用户定义变量是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用“@变量名”使用就可以。其作用域为当前连接(当前会话)。
- 两个@ 是系统变量
- 一个@ 是用户定义变量
select count(*) into @mycount from tb_user;
,把查询语句的结果赋值!!
-- 变量: 用户变量
-- 赋值
set @myname = 'itcast';
set @myage := 10;
set @mygender := '男',@myhobby := 'java';select @mycolor := 'red';
select count(*) into @mycount from tb_user;-- 使用
select @myname,@myage,@mygender,@myhobby;select @mycolor , @mycount;select @abc;
🐬在mysql中 =
,既是等于号,有事比较是否相等号,没有 ==
,所以在用户变量的时候,建议选:=
。
3.5 存储过程变量-局部变量
局部变量是根据需要定义的在局部生效的变量,访问之前,需要DECLARE声明。可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的BEGIN .. END
块。
🐬🐬🐬变量类型就是数据库字段类型: INT、BIGINT、 CHAR、 VARCHAR、DATE、 TIME等。
-- 变量: 局部变量
-- 声明 - declare
-- 赋值 -
create procedure p2()
begindeclare stu_count int default 0;select count(*) into stu_count from student;select stu_count;
end;call p2();
3.6 存储过程-if判断
-- if
-- 根据定义的分数score变量,判定当前分数对应的分数等级。
-- score >= 85分,等级为优秀。
-- score >= 60分 且 score < 85分,等级为及格。
-- score < 60分,等级为不及格。create procedure p3()
begindeclare score int default 58;declare result varchar(10);if score >= 85 thenset result := '优秀';elseif score >= 60 thenset result := '及格';elseset result := '不及格';end if;select result;
end;call p3();
3.7 存储过程- 参数(IN,OUT,INOUT)
类型 | 含义 | 备注 |
---|---|---|
IN | 该类参数作为输入,也就是需要调用时传入值 | 默认 |
OUT | 该类参数作为输出,也就是该参数可以作为返回值 | |
INOUT | 既可以作为输入参数,也可以作为输出参数 |
-- in/out/inout参数-- 根据传入(in)参数score,判定当前分数对应的分数等级,并返回(out)。
-- score >= 85分,等级为优秀。
-- score >= 60分 且 score < 85分,等级为及格。
-- score < 60分,等级为不及格。create procedure p4(in score int, out result varchar(10))
beginif score >= 85 thenset result := '优秀';elseif score >= 60 thenset result := '及格';elseset result := '不及格';end if;
end;call p4(18, @result);
select @result;-- 将传入的 200分制的分数,进行换算,换算成百分制 , 然后返回分数 ---> inout
create procedure p5(inout score double)
beginset score := score * 0.5;
end;set @score = 198;
call p5(@score);
select @score;
3.8 存储过程-case
-- case
-- 根据传入的月份,判定月份所属的季节(要求采用case结构)。
-- 1-3月份,为第一季度
-- 4-6月份,为第二季度
-- 7-9月份,为第三季度
-- 10-12月份,为第四季度create procedure p6(in month int)
begindeclare result varchar(10);casewhen month >= 1 and month <= 3 thenset result := '第一季度';when month >= 4 and month <= 6 thenset result := '第二季度';when month >= 7 and month <= 9 thenset result := '第三季度';when month >= 10 and month <= 12 thenset result := '第四季度';elseset result := '非法参数';end case ;select concat('您输入的月份为: ',month, ', 所属的季度为: ',result);
end;call p6(16);
3.9 存储过程- 循环-while
-- while 计算从1累加到n的值,n为传入的参数值。-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行减1 , 如果n减到0, 则退出循环
create procedure p7(in n int)
begindeclare total int default 0;while n>0 doset total := total + n;set n := n - 1;end while;select total;
end;call p7(100);
3.10 存储过程循环-repeat
-- repeat 计算从1累加到n的值,n为传入的参数值。
-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环
create procedure p8(in n int)
begindeclare total int default 0;repeatset total := total + n;set n := n - 1;until n <= 0end repeat;select total;
end;call p8(10);
call p8(100);
3.11 存储过程-循环-loop
#pic_center)
-- loop 计算从1累加到n的值,n为传入的参数值。
-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环 ----> leave xxcreate procedure p9(in n int)
begindeclare total int default 0;sum:loopif n<=0 thenleave sum;end if;set total := total + n;set n := n - 1;end loop sum;select total;
end;call p9(100);-- loop 计算从1到n之间的偶数累加的值,n为传入的参数值。
-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环 ----> leave xx
-- C. 如果当次累加的数据是奇数, 则直接进入下一次循环. --------> iterate xxcreate procedure p10(in n int)
begindeclare total int default 0;sum:loopif n<=0 thenleave sum;end if;if n%2 = 1 thenset n := n - 1;iterate sum;end if;set total := total + n;set n := n - 1;end loop sum;select total;
end;call p10(100);
3.12 存储过程游标-cursor
-- 通过变量记录查询结果
create procedure p11()
begindeclare stu_count int default 0;select * into stu_count from student;select stu_count;
end;call p11();
把一张表的数据复制给一个变量,不合适!!!我们之前学的只能是单行单列的
游标CURSOR是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括游标的声明、OPEN、 FETCH 和CLOSE,其语法分别如下。
变量和条件的申明要在游标之前
-- 游标
-- 根据传入的参数uage,来查询用户表tb_user中,
-- 所有的用户年龄小于等于uage的用户姓名(name)和专业(profession),
-- 并将用户的姓名和专业插入到所创建的一张新表(id,name,profession)中。-- 逻辑:
-- A. 声明游标, 存储查询结果集
-- B. 准备: 创建表结构
-- C. 开启游标
-- D. 获取游标中的记录
-- E. 插入数据到新表中
-- F. 关闭游标create procedure p11(in uage int)
begindeclare uname varchar(100);declare upro varchar(100);declare u_cursor cursor for select name,profession from tb_user where age <= uage;declare exit handler for SQLSTATE '02000' close u_cursor;drop table if exists tb_user_pro;create table if not exists tb_user_pro(id int primary key auto_increment,name varchar(100),profession varchar(100));open u_cursor;while true do-- 下行按照顺序来fetch u_cursor into uname,upro;insert into tb_user_pro values (null, uname, upro);end while;close u_cursor;end;call p11(30);
但是我的表是有的:
死循环的原因!!!!
3.13 存储过程条件处理程序-handler
条件处理程序(Handler) 可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。具体语法为:
declare exit handler for SQLSTATE '02000' close u_cursor;
create procedure p12(in uage int)
begindeclare uname varchar(100);declare upro varchar(100);declare u_cursor cursor for select name,profession from tb_user where age <= uage;declare exit handler for not found close u_cursor;drop table if exists tb_user_pro;create table if not exists tb_user_pro(id int primary key auto_increment,name varchar(100),profession varchar(100));open u_cursor;while true dofetch u_cursor into uname,upro;insert into tb_user_pro values (null, uname, upro);end while;close u_cursor;
end;call p12(30);
3.14 存储函数
存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。具体语法如下:
-- 存储函数
-- 从1到n的累加create function fun1(n int)
returns int deterministic
begindeclare total int default 0;while n>0 doset total := total + n;set n := n - 1;end while;return total;
end;select fun1(50);
4. 触发器
4.1 触发器-介绍
-- 触发器
-- 需求: 通过触发器记录 user 表的数据变更日志(user_logs) , 包含增加, 修改 , 删除 ;-- 准备工作 : 日志表 user_logs
create table user_logs(id int(11) not null auto_increment,operation varchar(20) not null comment '操作类型, insert/update/delete',operate_time datetime not null comment '操作时间',operate_id int(11) not null comment '操作的ID',operate_params varchar(500) comment '操作参数',primary key(`id`)
)engine=innodb default charset=utf8;
4.2 触发器-案例1(insert类型)
介绍
触发器是与表有关的数据库对象,指在insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。
触发器类型 | NEW和OLD |
---|---|
INSERT型触发器 | NEW表示将要或者已经新增的数据 |
UPDATE型触发器 | OLD表示修改之前的数据, NEW表示将要或已经修改后的数据 |
DELETE型触发器 | OLD表示将要或者已经删除的数据 |
删除可以指定数据库!!!
create table user_logs(id int(11) not null auto_increment,operation varchar(20) not null comment '操作类型, insert/update/delete',operate_time datetime not null comment '操作时间',operate_id int(11) not null comment '操作的ID',operate_params varchar(500) comment '操作参数',primary key(`id`)
)engine=innodb default charset=utf8;insert into tb_user(id, name, phone, email, profession, age, gender, status, createtime)
VALUES (25,'二皇子','18809091212','erhuangzi@163.com','软件工程',23,'1','1',now());update tb_user set age = 32 where id = 23;delete from tb_user where id = 25;
-- 插入数据触发器
create trigger tb_user_insert_triggerafter insert on tb_user for each row
begininsert into user_logs(id, operation, operate_time, operate_id, operate_params) VALUES(null, 'insert', now(), new.id, concat('插入的数据内容为: id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=', NEW.email, ', profession=', NEW.profession));
end;-- 查看
show triggers ;-- 删除
drop trigger tb_user_insert_trigger;-- 插入数据到tb_user
insert into tb_user(id, name, phone, email, profession, age, gender, status, createtime) VALUES (26,'三皇子','18809091212','erhuangzi@163.com','软件工程',23,'1','1',now());
这是查看的结果:
插入后的结果:
4.3 触发器-案例2(update类型)
-- 修改数据触发器
create trigger tb_user_update_triggerafter update on tb_user for each row
begininsert into user_logs(id, operation, operate_time, operate_id, operate_params) VALUES(null, 'update', now(), new.id,concat('更新之前的数据: id=',old.id,',name=',old.name, ', phone=', old.phone, ', email=', old.email, ', profession=', old.profession,' | 更新之后的数据: id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=', NEW.email, ', profession=', NEW.profession));
end;show triggers ;update tb_user set profession = '会计' where id = 23;update tb_user set profession = '会计' where id <= 5;
4.4 触发器-案例3(delete类型)
-- 删除数据触发器
create trigger tb_user_delete_triggerafter delete on tb_user for each row
begininsert into user_logs(id, operation, operate_time, operate_id, operate_params) VALUES(null, 'delete', now(), old.id,concat('删除之前的数据: id=',old.id,',name=',old.name, ', phone=', old.phone, ', email=', old.email, ', profession=', old.profession));
end;show triggers ;delete from tb_user where id = 26;
4.5 视图&存储过程&触发器-小结