更新了数据分析部分的文档

pull/820/merge
jackfrued 2021-11-06 20:41:44 +08:00
parent 2d5c4b5c2e
commit 08bc838dcd
10 changed files with 980 additions and 419 deletions

View File

@ -10,7 +10,7 @@
3. 关系数据库特点。
- 理论基础:**集合论**和**关系代数**。
- 理论基础:**关系代数**。
- 具体表象:用**二维表**(有行和列)组织数据。
@ -20,7 +20,7 @@
**ER模型**,全称为**实体关系模型**Entity-Relationship Model由美籍华裔计算机科学家陈品山先生提出是概念数据模型的高层描述方式如下图所示。
![](./res/er_diagram.png)
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210826003119.png" width="75%">
- 实体 - 矩形框
- 属性 - 椭圆框
@ -29,7 +29,7 @@
实际项目开发中我们可以利用数据库建模工具PowerDesigner来绘制概念数据模型其本质就是 ER 模型),然后再设置好目标数据库系统,将概念模型转换成物理模型,最终生成创建二维表的 SQL很多工具都可以根据我们设计的物理模型图以及设定的目标数据库来导出 SQL 或直接生成数据表)。
![](./res/conceptual_model.png)
![](https://gitee.com/jackfrued/mypic/raw/master/20210826003212.png)
5. 关系数据库产品。
- [Oracle](https://www.oracle.com/index.html) - 目前世界上使用最为广泛的数据库管理系统,作为一个通用的数据库系统,它具有完整的数据管理功能;作为一个关系数据库,它是一个完备关系的产品;作为分布式数据库,它实现了分布式处理的功能。在 Oracle 最新的 12c 版本中,还引入了多承租方架构,使用该架构可轻松部署和管理数据库云。
@ -40,23 +40,105 @@
### MySQL 简介
MySQL最早是由瑞典的MySQL AB公司开发的一个开放源码的关系数据库管理系统该公司于2008年被昇阳微系统公司Sun Microsystems收购。在2009年甲骨文公司Oracle收购昇阳微系统公司因此在这之后MySQL成为了Oracle旗下产品。
MySQL 最早是由瑞典的 MySQL AB 公司开发的一个开放源码的关系数据库管理系统该公司于2008年被昇阳微系统公司Sun Microsystems收购。在2009年甲骨文公司Oracle收购昇阳微系统公司因此 MySQL 目前也是 Oracle 旗下产品。
MySQL 在过去由于性能高、成本低、可靠性好,已经成为最流行的开源数据库,因此被广泛地应用于中小型网站开发。随着 MySQL 的不断成熟它也逐渐被应用于更多大规模网站和应用比如维基百科、谷歌Google、脸书Facebook、淘宝网等网站都使用了 MySQL 来提供数据持久化服务。
甲骨文公司收购后昇阳微系统公司大幅调涨MySQL商业版的售价且甲骨文公司不再支持另一个自由软件项目[OpenSolaris](https://zh.wikipedia.org/wiki/OpenSolaris)的发展因此导致自由软件社区对于Oracle是否还会持续支持MySQL社区版MySQL的各个发行版本中唯一免费的版本有所担忧MySQL的创始人麦克尔·维德纽斯以MySQL为基础成立分支计划[MariaDB](https://zh.wikipedia.org/wiki/MariaDB)以他女儿的名字命名的数据库。有许多原来使用MySQL数据库的公司例如维基百科已经陆续完成了从MySQL数据库到MariaDB数据库的迁移。
甲骨文公司收购后昇阳微系统公司,大幅调涨 MySQL 商业版的售价,且甲骨文公司不再支持另一个自由软件项目 [OpenSolaris ](https://zh.wikipedia.org/wiki/OpenSolaris) 的发展,因此导致自由软件社区对于 Oracle 是否还会持续支持 MySQL 社区版MySQL 的各个发行版本中唯一免费的版本有所担忧MySQL 的创始人麦克尔·维德纽斯以 MySQL 为基础,创建了 [MariaDB](https://zh.wikipedia.org/wiki/MariaDB)(以他女儿的名字命名的数据库)分支。有许多原来使用 MySQL 数据库的公司(例如:维基百科)已经陆续完成了从 MySQL 数据库到 MariaDB 数据库的迁移。
1. 安装和配置
### 安装 MySQL
> **说明**下面的安装和配置都是以CentOS Linux环境为例如果需要在其他系统下安装MySQL读者可以自行在网络上查找对应的安装教程
#### Windows 环境
- 刚才说过MySQL有一个分支版本名叫MariaDB该数据库旨在继续保持MySQL数据库在[GNU GPL](https://zh.wikipedia.org/wiki/GNU%E9%80%9A%E7%94%A8%E5%85%AC%E5%85%B1%E8%AE%B8%E5%8F%AF%E8%AF%81)下开源。如果要使用MariaDB作为MySQL的替代品可以使用下面的命令进行安装
1. 通过[官方网站](https://www.mysql.com/)提供的[下载链接](https://dev.mysql.com/downloads/windows/installer/8.0.html)下载“MySQL社区版服务器”安装程序如下图所示建议大家下载离线安装版的MySQL Installer
```Shell
yum install mariadb mariadb-server
```
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105230905.png" style="zoom:50%">
- 如果要安装官方版本的MySQL可以在[MySQL官方网站](<https://www.mysql.com/>)下载安装文件。首先在下载页面中选择平台和版本然后找到对应的下载链接。下面以MySQL 5.7.26版本和Red Hat Enterprise Linux为例直接下载包含所有安装文件的归档文件解归档之后通过包管理工具进行安装。
2. 运行 Installer按照下面的步骤进行安装。
- 选择自定义安装。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105231152.jpg" style="zoom:35%">
- 选择需要安装的组件。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105231255.jpg" style="zoom:35%">
- 如果缺少依赖项,需要先安装依赖项。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105231620.png" style="zoom:35%">
- 准备开始安装。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105231719.jpg" style="zoom:35%">
- 安装完成。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232024.jpg" style="zoom:35%">
- 准备执行配置向导。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105231815.jpg" style="zoom:35%">
3. 执行安装后的配置向导。
- 配置服务器类型和网络。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232109.jpg" style="zoom:35%">
- 配置认证方法(保护密码的方式)。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232408.jpg" style="zoom:35%">
- 配置用户和角色。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232521.jpg" style="zoom:35%">
- 配置Windows服务名以及是否开机自启。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232608.jpg" style="zoom:35%">
- 配置日志。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232641.jpg" style="zoom:35%">
- 配置高级选项。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232724.jpg" alt="ACAC15B8633133B65476286A49BFBD7E" style="zoom:35%">
- 应用配置。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232800.jpg" style="zoom:35%">
4. 可以在 Windows 系统的“服务”窗口中启动或停止 MySQL。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105232926.jpg" style="zoom:50%">
5. 配置 PATH 环境变量,以便在命令行提示符窗口使用 MySQL 客户端工具。
- 打开 Windows 的“系统”窗口并点击“高级系统设置”。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105233054.jpg" style="zoom:50%">
- 在“系统属性”的“高级”窗口,点击“环境变量”按钮。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105233312.jpg" style="zoom:50%">
- 修改PATH环境变量将MySQL安装路径下的`bin`文件夹的路径配置到PATH环境变量中。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105233359.jpg" style="zoom:50%">
- 配置完成后,可以尝试在“命令提示符”下使用 MySQL 的命令行工具。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211105233643.jpg" style="zoom:50%">
#### Linux 环境
下面以 CentOS 7.x 环境为例,演示如何安装 MySQL 5.7.x如果需要在其他 Linux 系统下安装其他版本的 MySQL请读者自行在网络上查找对应的安装教程。
1. 安装 MySQL。
可以在 [MySQL 官方网站](<https://www.mysql.com/>)下载安装文件。首先在下载页面中选择平台和版本,然后找到对应的下载链接,直接下载包含所有安装文件的归档文件,解归档之后通过包管理工具进行安装。
```Shell
wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar
@ -69,6 +151,13 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
yum list installed | grep mariadb | awk '{print $1}' | xargs yum erase -y
```
更新和安装可能用到的底层依赖库。
```Bash
yum update
yum install -y libaio libaio-devel
```
接下来可以按照如下所示的顺序用 RPMRedhat Package Manager工具安装 MySQL。
```Shell
@ -86,9 +175,9 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
rpm -qa | grep mysql
```
- 配置MySQL。
2. 配置 MySQL。
MySQL的配置文件在`/etc`目录下,名为`my.cnf`,默认的配置文件内容如下所示。如果对这个文件不理解并没有关系,什么时候用到这个配置文件什么时候再了解它就行了。
MySQL 的配置文件在`/etc`目录下,名为`my.cnf`,默认的配置文件内容如下所示。
```Shell
cat /etc/my.cnf
@ -124,7 +213,9 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
pid-file=/var/run/mysqld/mysqld.pid
```
- 启动MySQL服务。
通过配置文件,我们可以修改 MySQL 服务使用的端口、字符集、最大连接数、套接字队列大小、最大数据包大小、日志文件的位置、日志过期时间等配置。当然,我们还可以通过修改配置文件来对 MySQL 服务器进行性能调优和安全管控。
3. 启动 MySQL 服务。
可以使用下面的命令来启动 MySQL。
@ -138,19 +229,19 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
systemctl start mysqld
```
启动MySQL成功后可以通过下面的命令来检查网络端口使用情况MySQL默认使用3306端口。
启动 MySQL 成功后可以通过下面的命令来检查网络端口使用情况MySQL 默认使用`3306`端口。
```Shell
netstat -ntlp | grep mysql
```
也可以使用下面的命令查找是否有名为mysqld的进程。
也可以使用下面的命令查找是否有名为`mysqld`的进程。
```Shell
pgrep mysqld
```
- 使用MySQL客户端工具连接服务器。
4. 使用 MySQL 客户端工具连接服务器。
命令行工具:
@ -166,9 +257,9 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
cat /var/log/mysqld.log | grep password
```
上面的命令会查看MySQL的日志带有password的行在显示的结果中`root@localhost:`后面的部分就是默认设置的初始密码。
上面的命令会查看 MySQL 的日志带有`password`的行,在显示的结果中`root@localhost:`后面的部分就是默认设置的初始密码。
修改超级管理员root的访问口令为`123456`。
进入客户端工具后,可以通过下面的指令来修改超级管理员root的访问口令为`123456`。
```SQL
set global validate_password_policy=0;
@ -176,21 +267,19 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
alter user 'root'@'localhost' identified by '123456';
```
> **说明**MySQL较新的版本默认不允许使用弱口令作为用户口令所以我们通过上面的前两条命令修改了验证用户口令的策略和口令的长度。事实上我们不应该使用弱口令,因为存在用户口令被暴力破解的风险。近年来,攻击数据库窃取数据和劫持数据库勒索比特币的事件屡见不鲜,要避免这些潜在的风险,最为重要的一点是不要让数据库服务器暴露在公网上(最好的做法是将数据库置于内网,至少要做到不向公网开放数据库服务器的访问端口),另外要保管好`root`账号的口令,应用系统需要访问数据库时,通常不使用`root`账号进行访问,而是创建其他拥有适当权限的账号来访问。
> **说明**MySQL 较新的版本默认不允许使用弱口令作为用户口令,所以上面的代码修改了验证用户口令的策略和口令的长度。事实上我们不应该使用弱口令,因为存在用户口令被暴力破解的风险。近年来,**攻击数据库窃取数据和劫持数据库勒索比特币**的事件屡见不鲜,要避免这些潜在的风险,最为重要的一点是**不要让数据库服务器暴露在公网上**(最好的做法是将数据库置于内网,至少要做到不向公网开放数据库服务器的访问端口),另外要保管好`root`账号的口令,应用系统需要访问数据库时,通常不使用`root`账号进行访问,而是**创建其他拥有适当权限的账号来访问**
再次使用客户端工具连接 MySQL 服务器时,就可以使用新设置的口令了。在实际开发中,为了方便用户操作,可以选择图形化的客户端工具来连接 MySQL 服务器,包括:
- MySQL Workbench官方提供的工具
- Navicat for MySQL界面简单功能直观
- MySQL Workbench官方工具推荐大家使用
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106063939.png" style="zoom:50%">
- Navicat for MySQL界面简单清爽功能直观
- SQLyog for MySQL强大的MySQL数据库管理员工具
2. 常用命令。
- 查看服务器版本。
```SQL
select version();
```
### MySQL 常用命令
- 查看所有数据库。
@ -198,33 +287,65 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
show databases;
```
- 切换到指定数据库
- 查看所有字符集(编码方式)
```SQL
use mysql;
show character set;
```
- 查看所有的校对(排序)规则。
```SQL
show collation;
```
- 查看所有的引擎。
```SQL
show engines;
```
- 查看所有日志文件。
```SQL
show binary logs;
```
- 查看数据库下所有表。
```Shell
```SQL
show tables;
```
- 获取帮助。
```SQL
? contents;
? functions;
? numeric functions;
? round;
1. 查看`show`命令的帮助。
? data types;
? longblob;
```MySQL
? show
```
2. 查看有哪些帮助内容。
```MySQL
? contents
```
3. 获取函数的帮助。
```MySQL
? functions
```
4. 获取数据类型的帮助。
```MySQL
? data types
```
### SQL 详解
我们通常可以将SQL分为三类DDL数据定义语言、DML数据操作语言和DCL数据控制语言。DDL主要用于创建create、删除drop、修改alter数据库中的对象比如创建、删除和修改二维表DML主要负责插入数据insert、删除数据delete、更新数据update和查询selectDCL通常用于授予权限grant和召回权限revoke
我们通常可以将 SQL 分为四类,分别是 DDL数据定义语言、DML数据操作语言、DQL数据查询语言 DCL数据控制语言。DDL 主要用于创建、删除、修改数据库中的对象,比如创建、删除和修改二维表,核心的关键字包括`create`、`drop`和`alter`DML 主要负责数据的插入、删除和更新,关键词包括`insert`、`delete`和`update`DQL 负责数据查询,最重要的一个关键词是`select`DCL 通常用于授予和召回权限,核心关键词是`grant`和`revoke`
> **说明**SQL 是不区分大小写的语言,为了书写和识别方便,下面的 SQL 都使用了小写字母来书写。
@ -235,7 +356,7 @@ MySQL在过去由于性能高、成本低、可靠性好已经成为最流行
drop database if exists `school`;
-- 创建名为school的数据库并设置默认的字符集和排序方式
create database `school` default character set utf8mb4;
create database `school` default character set utf8mb4 collate utf8mb4_general_ci;
-- 切换到school数据库上下文环境
use `school`;
@ -245,21 +366,21 @@ create table `tb_college`
(
`col_id` int unsigned auto_increment comment '编号',
`col_name` varchar(50) not null comment '名称',
`col_intro` varchar(5000) default '' comment '介绍',
`col_intro` varchar(500) default '' comment '介绍',
primary key (`col_id`)
) engine=innodb comment '学院表';
) engine=innodb auto_increment=1 comment '学院表';
-- 创建学生表
create table `tb_student`
(
`stu_id` int unsigned not null comment '学号',
`stu_name` varchar(20) not null comment '姓名',
`stu_sex` boolean default 1 comment '性别',
`stu_sex` boolean default 1 not null comment '性别',
`stu_birth` date not null comment '出生日期',
`stu_addr` varchar(255) default '' comment '籍贯',
`col_id` int unsigned not null comment '所属学院',
primary key (`stu_id`),
foreign key (`col_id`) references `tb_college` (`col_id`)
constraint `fk_student_col_id` foreign key (`col_id`) references `tb_college` (`col_id`)
) engine=innodb comment '学生表';
-- 创建教师表
@ -270,7 +391,7 @@ create table `tb_teacher`
`tea_title` varchar(10) default '助教' comment '职称',
`col_id` int unsigned not null comment '所属学院',
primary key (`tea_id`),
foreign key (`col_id`) references `tb_college` (`col_id`)
constraint `fk_teacher_col_id` foreign key (`col_id`) references `tb_college` (`col_id`)
) engine=innodb comment '老师表';
-- 创建课程表
@ -278,30 +399,30 @@ create table `tb_course`
(
`cou_id` int unsigned not null comment '编号',
`cou_name` varchar(50) not null comment '名称',
`cou_credit` int unsigned not null comment '学分',
`cou_credit` int not null comment '学分',
`tea_id` int unsigned not null comment '授课老师',
primary key (`cou_id`),
foreign key (`tea_id`) references `tb_teacher` (`tea_id`)
constraint `fk_course_tea_id` foreign key (`tea_id`) references `tb_teacher` (`tea_id`)
) engine=innodb comment '课程表';
-- 创建选课记录表
create table `tb_record`
(
`rec_id` bigint unsigned auto_increment comment '选课记录号',
`sid` int unsigned not null comment '学号',
`cid` int unsigned not null comment '课程编号',
`stu_id` int unsigned not null comment '学号',
`cou_id` int unsigned not null comment '课程编号',
`sel_date` date not null comment '选课日期',
`score` decimal(4,1) comment '考试成绩',
primary key (`rec_id`),
foreign key (`sid`) references `tb_student` (`stu_id`),
foreign key (`cid`) references `tb_course` (`cou_id`),
unique (`sid`, `cid`)
constraint `fk_record_stu_id` foreign key (`stu_id`) references `tb_student` (`stu_id`),
constraint `fk_record_cou_id` foreign key (`cou_id`) references `tb_course` (`cou_id`),
constraint `uk_record_stu_cou` unique (`stu_id`, `cou_id`)
) engine=innodb comment '选课记录表';
```
上面的DDL有几个地方需要强调一下
- 创建数据库时,我们通过`default charset utf8`指定了数据库默认使用的字符集我们推荐使用该字符集因为utf8能够支持国际化编码。如果将来数据库中用到的字符可能包括类似于Emoji这样的图片字符也可以将默认字符集设定为utf8mb4最大4字节的utf-8编码。查看MySQL支持的字符集可以执行下面的语句
- 创建数据库时,我们通过`default character set utf8mb4`指定了数据库默认使用的字符集为`utf8mb4`(最大`4`字节的`utf-8`编码),我们推荐使用该字符集,它也是 MySQL 8.x 默认使用的字符集,因为它能够支持国际化编码,还可以存储 Emoji 字符。可以通过下面的命令查看 MySQL 支持的字符集以及默认的排序规则
```SQL
show character set;
@ -356,14 +477,14 @@ unique (`sid`, `cid`)
41 rows in set (0.00 sec)
```
如果要设置MySQL服务启动时默认使用的字符集可以修改MySQL的配置并添加以下内容
如果要设置 MySQL 服务启动时默认使用的字符集可以修改MySQL的配置并添加以下内容
```INI
[mysqld]
character-set-server=utf8
```
- 在创建表的时候,我们可以在右圆括号的后面通过`engine=XXX`来指定表的存储引擎MySQL支持多种存储引擎,可以通过`show engines`命令进行查看。MySQL 5.5以后的版本默认使用的存储引擎是InnoDB正好也就是我们推荐大家使用的存储引擎(因为InnoDB更适合互联网应用对高并发、性能以及事务支持等方面的需求)。
- 在创建表的时候,可以自行选择底层的存储引擎。MySQL 支持多种存储引擎,可以通过`show engines`命令进行查看。MySQL 5.5 以后的版本默认使用的存储引擎是 InnoDB它是我们推荐大家使用的存储引擎因为更适合当下互联网应用对高并发、性能以及事务支持等方面的需求),为了 SQL 语句的向下兼容性,我们可以在建表语句结束处右圆括号的后面通过`engine=innodb`来指定使用 InnoDB 存储引擎
```SQL
show engines\G
@ -455,7 +576,7 @@ unique (`sid`, `cid`)
| 批量插入性能 | 低 | 高 | 高 | 高 |
| 是否支持外键 | 支持 | | | |
通过上面的比较我们可以了解到InnoDB是唯一能够支持外键、事务以及行锁的存储引擎所以我们之前说它更适合互联网应用而且它也是较新的MySQL版本中默认使用的存储引擎。
通过上面的比较我们可以了解到InnoDB 是唯一能够支持外键、事务以及行锁的存储引擎,所以我们之前说它更适合互联网应用,而且在较新版本的 MySQL 中,它也是默认使用的存储引擎。
- 在定义表结构为每个字段选择数据类型时,如果不清楚哪个数据类型更合适,可以通过 MySQL 的帮助系统来了解每种数据类型的特性、数据的长度和精度等相关信息。
@ -542,7 +663,7 @@ unique (`sid`, `cid`)
URL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
```
在数据类型的选择上保存字符串数据通常都使用VARCHAR和CHAR两种类型前者通常称为变长字符串而后者通常称为定长字符串对于InnoDB存储引擎行存储格式没有区分固定长度和可变长度列因此VARCHAR类型好CHAR类型没有本质区别后者不一定比前者性能更好。如果要保存的很大字符串可以使用TEXT类型如果要保存很大的字节串可以使用BLOB二进制大对象类型。在MySQL中TEXT和BLOB又分别包括TEXT、MEDIUMTEXT、LONGTEXT和BLOB、MEDIUMBLOB、LONGBLOB三种不同的类型它们主要的区别在于存储数据的最大大小不同。保存浮点数可以用FLOAT或DOUBLE类型而保存定点数应该使用DECIMAL类型。如果要保存时间日期DATETIME类型优于TIMESTAMP类型因为前者能表示的时间日期范围更大。
在数据类型的选择上,保存字符串数据通常都使用`VARCHAR``CHAR`两种类型,前者通常称为变长字符串,而后者通常称为定长字符串;对于 InnoDB 存储引擎,行存储格式没有区分固定长度和可变长度列,因此`VARCHAR`类型和`CHAR`类型没有本质区别,后者不一定比前者性能更好。如果要保存的很大字符串,可以使用`TEXT`类型;如果要保存很大的字节串,可以使用`BLOB`(二进制大对象)类型。在 MySQL 中,`TEXT``BLOB`又分别包括`TEXT``MEDIUMTEXT``LONGTEXT``BLOB``MEDIUMBLOB``LONGBLOB`三种不同的类型,它们主要的区别在于存储数据的最大大小不同。保存浮点数可以用`FLOAT``DOUBLE`类型,`FLOAT`已经不推荐使用了,而且在 MySQL 后续的版本中可能会被移除掉。而保存定点数应该使用`DECIMAL`类型。如果要保存时间日期,`DATETIME`类型优于`TIMESTAMP`类型,因为前者能表示的时间日期范围更大。
#### DML数据操作语言
@ -598,7 +719,7 @@ values
-- 插入选课数据
insert into `tb_record`
(`sid`, `cid`, `sel_date`, `score`)
(`stu_id`, `cou_id`, `sel_date`, `score`)
values
(1001, 1111, '2017-09-01', 95),
(1001, 2222, '2017-09-01', 87.5),
@ -625,83 +746,52 @@ values
```SQL
-- 查询所有学生的所有信息
select * from tb_student;
select stu_id, stu_name, stu_sex, stu_birth, stu_addr, col_id from tb_student;
-- 查询所有课程名称及学分(投影和别名)
-- 查询学生的学号、姓名和家庭住址(投影)
select stu_id, stu_name, stu_addr from tb_student;
-- 查询所有课程的名称及学分(投影和别名)
select cou_name as 课程名称, cou_credit as 学分 from tb_course;
-- 查询所有女学生的姓名和出生日期(筛选)
select stu_name, stu_birth from tb_student where stu_sex=0;
-- 查询所有80后学生的姓名、性别和出生日期(筛选)
select stu_name, stu_sex, stu_birth from tb_student
where stu_birth>='1980-1-1' and stu_birth<='1989-12-31';
select stu_name, stu_sex, stu_birth from tb_student where stu_birth>='1980-1-1' and stu_birth<='1989-12-31';
select stu_name, stu_sex, stu_birth from tb_student
where stu_birth between '1980-1-1' and '1989-12-31';
select stu_name, stu_sex, stu_birth from tb_student where stu_birth between '1980-1-1' and '1989-12-31';
-- 补充1在查询时可以对列的值进行处理
select
stu_name as 姓名,
case stu_sex when 1 then '男' else '女' end as 性别,
stu_birth as 生日
from tb_student
where stu_birth between '1980-1-1' and '1989-12-31';
-- 将表示性别的 1 和 0 处理成 “男” 和 “女”
select stu_name as 姓名, case stu_sex when 1 then '男' else '女' end as 性别, stu_birth as 生日 from tb_student where stu_birth between '1980-1-1' and '1989-12-31';
-- 补充2MySQL方言(使用数据库特有的函数)
-- 例如Oracle中做同样事情的函数叫做decode
select
stu_name as 姓名,
if(stu_sex, '男', '女') as 性别,
stu_birth as 生日
from tb_student
where stu_birth between '1980-1-1' and '1989-12-31';
-- 查询所有80后女学生的姓名和出生日期
select stu_name, stu_birth from tb_student
where stu_birth between '1980-1-1' and '1989-12-31' and stu_sex=0;
-- 查询所有的80后学生或女学生的姓名和出生日期
select stu_name, stu_birth from tb_student
where stu_birth between '1980-1-1' and '1989-12-31' or stu_sex=0;
select stu_name as 姓名, if(stu_sex, '男', '女') as 性别, stu_birth as 生日 from tb_student where stu_birth between '1980-1-1' and '1989-12-31';
-- 查询姓“杨”的学生姓名和性别(模糊)
-- 在SQL中通配符%可以匹配零个或任意多个字符
select stu_name, stu_sex from tb_student where stu_name like '杨%';
-- 查询姓“杨”名字两个字的学生姓名和性别(模糊)
-- 在SQL中通配符_可以刚刚好匹配一个字符
select stu_name, stu_sex from tb_student where stu_name like '杨_';
-- 查询姓“杨”名字三个字的学生姓名和性别(模糊)
select stu_name, stu_sex from tb_student where stu_name like '杨__';
-- 查询名字中有“不”字或“嫣”字的学生的姓名(模糊)
-- 提示:前面带%的模糊查询性能基本上都是非常糟糕的
select stu_name from tb_student
where stu_name like '%不%' or stu_name like '%嫣%';
select stu_name from tb_student where stu_name like '%不%' or stu_name like '%嫣%';
update tb_student set stu_name='岳不嫣' where stu_id=1572;
-- 并集运算
select stu_name from tb_student where stu_name like '%不%'
select stu_name from tb_student where stu_name like '%嫣%'
union
select stu_name from tb_student where stu_name like '%%';
select stu_name from tb_student where stu_name like '%不%';
select stu_name from tb_student where stu_name like '%不%'
union all
select stu_name from tb_student where stu_name like '%嫣%';
-- 正则表达式模糊查询
select stu_name, stu_sex from tb_student where stu_name regexp '^杨.{2}$';
-- 查询姓“杨”或姓“林”名字三个字的学生的姓名(正则表达式模糊查询)
select stu_name from tb_student where stu_name regexp '[杨林].{2}';
-- 查询没有录入家庭住址的学生姓名(空值)
-- null作任何运算结果也是产生nullnull相当于是条件不成立
select stu_name from tb_student where stu_addr is null;
select stu_name from tb_student where stu_addr<=>null;
select stu_name from tb_student where stu_addr is null or stu_add='';
select stu_name from tb_student where stu_addr<=>null or stu_addr='';
-- 查询录入了家庭住址的学生姓名(空值)
select stu_name from tb_student where stu_addr is not null;
select stu_name from tb_student where stu_addr is not null and stu_addr<>'';
-- 查询学生选课的所有日期(去重)
select distinct sel_date from tb_record;
@ -710,197 +800,180 @@ select distinct sel_date from tb_record;
select distinct stu_addr from tb_student where stu_addr is not null;
-- 查询男学生的姓名和生日按年龄从大到小排列(排序)
-- asc - 升序从小到大desc - 降序(从大到小)
select stu_name, stu_birth from tb_student
where stu_sex=1 order by stu_birth asc;
select stu_name, stu_birth from tb_student where stu_sex=1 order by stu_birth asc;
select stu_name, stu_birth from tb_student
where stu_sex=1 order by stu_birth desc;
-- 将生日换算成年龄(日期函数、数值函数)
select stu_name, stu_birth, floor(datediff(curdate(), stu_birth)/365) as stu_age from tb_student where stu_sex=1 order by stu_age desc;
-- 查询年龄最大的学生的出生日期(聚合函数) ---> 找出最小的生日
-- 查询年龄最大的学生的出生日期(聚合函数)
select min(stu_birth) from tb_student;
select
min(stu_birth) as 生日,
floor(datediff(curdate(), min(stu_birth))/365) as 年龄
from tb_student;
-- 查询年龄最小的学生的出生日期(聚合函数)
select
max(stu_birth) as 生日,
floor(datediff(curdate(), max(stu_birth))/365) as 年龄
from tb_student;
select max(stu_birth) from tb_student;
-- 查询所有考试的平均成绩
-- 聚合函数在遇到null值会做忽略的处理
-- 如果做计数操作建议使用count(*),这样才不会漏掉空值
select avg(score) from tb_record;
-- 查询编号为1111的课程考试成绩的最高分
select max(score) from tb_record where cou_id=1111;
select sum(score) / count(score) from tb_record;
-- 查询学号为1001的学生考试成绩的最低分
select min(score) from tb_record where stu_id=1001;
select sum(score) / count(*) from tb_record;
-- 查询学号为1001的学生考试成绩的平均分
select avg(score) from tb_record where stu_id=1001;
select sum(score) / count(score) from tb_record where stu_id=1001;
-- 查询课程编号为1111的课程的平均成绩(筛选和聚合函数)
select avg(score) from tb_record where cid=1111;
-- 查询学号为1001的学生考试成绩的平均分如果有null值null值算0分
select sum(score) / count(*) from tb_record where stu_id=1001;
select avg(ifnull(score, 0)) from tb_record where stu_id=1001;
-- 查询学号为1001的学生所有课程的平均分(筛选和聚合函数)
select avg(score) from tb_record where sid=1001;
select count(distinct stu_addr) from tb_student where stu_addr is not null;
-- 查询学号为1001的学生考试成绩的标准差
select std(score) from tb_record where stu_id=1001;
-- 查询男女学生的人数(分组和聚合函数)
-- SACSplit - Aggregate - Combine
select
if(stu_sex, '男', '女') as 性别,
count(*) as 人数
from tb_student group by stu_sex;
select if(stu_sex, '男', '女') as 性别, count(*) as 人数 from tb_student group by stu_sex;
-- 统计每个学院男女学生的人数
select
col_id as 学院,
if(stu_sex, '男', '女') as 性别,
count(*) as 人数
from tb_student group by col_id, stu_sex;
-- 查询每个学院男女学生人数
select col_id as 学院编号, if(stu_sex, '男', '女') as 性别, count(*) as 人数 from tb_student group by col_id, stu_sex;
-- 查询每个学生的学号和平均成绩(分组和聚合函数)
select
sid as 学号,
round(avg(score),1) as 平均分
from tb_record group by sid;
select stu_id as 学号, round(avg(score), 2) as 平均分 from tb_record group by stu_id;
-- 查询平均成绩大于等于90分的学生的学号和平均成绩
-- 分组以前的数据筛选使用where子句分组以后的数据筛选使用having子句
select
sid as 学号,
round(avg(score),1) as 平均分
from tb_record
group by sid having 平均分>=90;
select stu_id as 学号, round(avg(score), 2) as 平均分 from tb_record group by stu_id having 平均分>=90;
-- 查询1111、2222、3333三门课程平均成绩大于等于90分的学生的学号和平均成绩
select stu_id as 学号, round(avg(score), 2) as 平均分 from tb_record where cou_id in (1111, 2222, 3333) group by stu_id having 平均分>=90;
-- 查询年龄最大的学生的姓名(子查询)
-- 嵌套查询:把一个查询的结果作为另外一个查询的一部分来使用。
select stu_name from tb_student where stu_birth=(
select min(stu_birth) from tb_student
);
-- 查询年龄最大的学生姓名和年龄(子查询+运算)
select
stu_name as 姓名,
floor(datediff(curdate(), stu_birth) / 365) as 年龄
from tb_student where stu_birth=(
select min(stu_birth) from tb_student
);
select stu_name from tb_student where stu_birth=(select min(stu_birth) from tb_student);
-- 查询选了两门以上的课程的学生姓名(子查询/分组条件/集合运算)
select stu_name from tb_student where stu_id in (
select sid from tb_record group by sid having count(*)>2
);
select stu_name from tb_student where stu_id in (select stu_id from tb_record group by stu_id having count(*)>2);
-- 查询课程的名称、学分和授课老师的姓名(连接查询)
select cou_name, cou_credit, tea_name
from tb_course, tb_teacher
where tb_course.tea_id=tb_teacher.tea_id;
-- 查询学生的姓名、生日和所在学院名称
select stu_name, stu_birth, col_name from tb_student, tb_college where tb_student.col_id=tb_college.col_id;
select cou_name, cou_credit, tea_name from tb_course t1
inner join tb_teacher t2 on t1.tea_id=t2.tea_id;
select stu_name, stu_birth, col_name from tb_student t1 inner join tb_college t2 on t1.col_id=t2.col_id;
-- 查询学生姓名、课程名称以及成绩(连接查询)
select stu_name, cou_name, score
from tb_record, tb_student, tb_course
where stu_id=sid and cou_id=cid and score is not null;
select stu_name, stu_birth, col_name from tb_student natural join tb_college;
select stu_name, cou_name, score from tb_student
inner join tb_record on stu_id=sid
inner join tb_course on cou_id=cid
where score is not null;
-- 查询学生姓名、课程名称以及成绩(连接查询/联结查询)
select t2.stu_id, stu_name, t3.cou_id, cou_name, score from tb_record t1, tb_student t2, tb_course t3 where t1.stu_id=t2.stu_id and t1.cou_id=t3.cou_id and score is not null;
select stu_name, cou_name, score from tb_student t1 inner join tb_record t2 on t1.stu_id=t2.stu_id inner join tb_course t3 on t2.cou_id=t3.cou_id where score is not null;
select stu_name, cou_name, score from tb_student natural join tb_record natural join tb_course where score is not null;
-- 分页查询(前5条数据)
select stu_name, cou_name, score from tb_student natural join tb_record natural join tb_course where score is not null order by score desc limit 5;
-- 分页查询(6-10条数据)
select stu_name, cou_name, score from tb_student natural join tb_record natural join tb_course where score is not null order by score desc limit 5 offset 5;
-- 分页查询(11-15条数据)
select stu_name, cou_name, score from tb_student natural join tb_record natural join tb_course where score is not null order by score desc limit 5 offset 10;
select stu_name, cou_name, score from tb_student natural join tb_record natural join tb_course where score is not null order by score desc limit 10,5;
-- 查询选课学生的姓名和平均成绩(子查询和连接查询)
select stu_name, avg_score
from tb_student, (select sid, round(avg(score),1) as avg_score
from tb_record group by sid
) tb_temp where stu_id=sid;
select stu_name, avg_score from tb_student t1, (select stu_id, round(avg(score),1) as avg_score from tb_record group by stu_id) t2 where t1.stu_id=t2.stu_id;
-- 查询学生的姓名和选课的数量
select stu_name, total from tb_student t1, (select stu_id, count(*) as total from tb_record group by stu_id) t2 where t1.stu_id=t2.stu_id;
-- 查询每个学生的姓名和选课数量(左外连接和子查询)
select
stu_name as 姓名,
ifnull(total, 0) as 选课数量
from tb_student left outer join (
select sid, count(*) as total from tb_record group by sid
) tb_temp on stu_id=sid;
select stu_name, ifnull(total, 0) as total from tb_student t1 left outer join (select stu_id, count(*) as total from tb_record group by stu_id) t2 on t1.stu_id=t2.stu_id;
-- 删除tb_record表的外键约束
alter table tb_record drop foreign key fk_record_stu_id;
alter table tb_record drop foreign key fk_record_cou_id;
-- 给tb_record表加两条记录学号5566在学生表没有对应的记录
insert into tb_record
values
(default, 5566, 1111, '2019-09-02', 80),
(default, 5566, 2222, '2019-09-02', 70);
-- 查询学生的姓名和选课数量(右外连接)
select t1.stu_id, stu_name, t2.stu_id, total as total from tb_student t1 right outer join (select stu_id, count(*) as total from tb_record group by stu_id) t2 on t1.stu_id=t2.stu_id;
-- 可以通过左外连接与右外连接求并集运算得到全外连接的结果
select t1.stu_id, stu_name, t2.stu_id, total as total from tb_student t1 left outer join (select stu_id, count(*) as total from tb_record group by stu_id) t2 on t1.stu_id=t2.stu_id
union
select t1.stu_id, stu_name, t2.stu_id, total as total from tb_student t1 right outer join (select stu_id, count(*) as total from tb_record group by stu_id) t2 on t1.stu_id=t2.stu_id;
```
上面的DML有几个地方需要加以说明
1. MySQL中支持多种类型的运算符包括算术运算符+、-、*、/、%)、比较运算符(=、<>、<=>、<<=、>、>=、BETWEEN...AND...、IN、IS NULL、IS NOT NULL、LIKE、RLIKE、REGEXP、逻辑运算符NOT、AND、OR、XOR和位运算符&、|、^、~、>>、<<我们可以在DML中使用这些运算符处理数据。
1. MySQL 中支持多种类型的运算符,包括:算术运算符(`+``-``*``/``%`)、比较运算符(`=``<>``<=>``<``<=``>``>=``BETWEEN...AND..`.、`IN``IS NULL``IS NOT NULL``LIKE``RLIKE``REGEXP`)、逻辑运算符(`NOT``AND``OR``XOR`)和位运算符(`&``|``^``~``>>``<<`),我们可以在 DML 中使用这些运算符处理数据。
2. 在查询数据时可以在SELECT语句及其子句如WHERE子句、ORDER BY子句、HAVING子句等中使用函数这些函数包括字符串函数、数值函数、时间日期函数、流程函数等如下面的表格所示。
2. 在查询数据时,可以在`SELECT`语句及其子句(如`WHERE`子句、`ORDER BY`子句、`HAVING`子句等)中使用函数,这些函数包括字符串函数、数值函数、时间日期函数、流程函数等,如下面的表格所示。
常用字符串函数。
| 函数 | 功能 |
| ----------------------- | ----------------------------------------------------- |
| CONCAT | 将多个字符串连接成一个字符串 |
| FORMAT | 将数值格式化成字符串并指定保留几位小数 |
| FROM_BASE64 / TO_BASE64 | BASE64解码/编码 |
| BIN / OCT / HEX | 将数值转换成二进制/八进制/十六进制字符串 |
| LOCATE | 在字符串中查找一个子串的位置 |
| LEFT / RIGHT | 返回一个字符串左边/右边指定长度的字符 |
| LENGTH / CHAR_LENGTH | 返回字符串的长度以字节/字符为单位 |
| LOWER / UPPER | 返回字符串的小写/大写形式 |
| LPAD / RPAD | 如果字符串的长度不足,在字符串左边/右边填充指定的字符 |
| LTRIM / RTRIM | 去掉字符串前面/后面的空格 |
| ORD / CHAR | 返回字符对应的编码/返回编码对应的字符 |
| STRCMP | 比较字符串,返回-1、0、1分别表示小于、等于、大于 |
| SUBSTRING | 返回字符串指定范围的子串 |
| --------------------------- | ----------------------------------------------------- |
| `CONCAT` | 将多个字符串连接成一个字符串 |
| `FORMAT` | 将数值格式化成字符串并指定保留几位小数 |
| `FROM_BASE64` / `TO_BASE64` | BASE64解码/编码 |
| `BIN` / `OCT` / `HEX` | 将数值转换成二进制/八进制/十六进制字符串 |
| `LOCATE` | 在字符串中查找一个子串的位置 |
| `LEFT` / `RIGHT` | 返回一个字符串左边/右边指定长度的字符 |
| `LENGTH` / `CHAR_LENGTH` | 返回字符串的长度以字节/字符为单位 |
| `LOWER` / `UPPER` | 返回字符串的小写/大写形式 |
| `LPAD` / `RPAD` | 如果字符串的长度不足,在字符串左边/右边填充指定的字符 |
| `LTRIM` / `RTRIM` | 去掉字符串前面/后面的空格 |
| `ORD` / `CHAR` | 返回字符对应的编码/返回编码对应的字符 |
| `STRCMP` | 比较字符串,返回-1、0、1分别表示小于、等于、大于 |
| `SUBSTRING` | 返回字符串指定范围的子串 |
常用数值函数。
| 函数 | 功能 |
| ------------------------------------------ | ---------------------------------- |
| ABS | 返回一个数的绝度值 |
| CEILING / FLOOR | 返回一个数上取整/下取整的结果 |
| CONV | 将一个数从一种进制转换成另一种进制 |
| CRC32 | 计算循环冗余校验码 |
| EXP / LOG / LOG2 / LOG10 | 计算指数/对数 |
| POW | 求幂 |
| RAND | 返回[0,1)范围的随机数 |
| ROUND | 返回一个数四舍五入后的结果 |
| SQRT | 返回一个数的平方根 |
| TRUNCATE | 截断一个数到指定的精度 |
| SIN / COS / TAN / COT / ASIN / ACOS / ATAN | 三角函数 |
| -------------------------------------------------------- | ---------------------------------- |
| `ABS` | 返回一个数的绝度值 |
| `CEILING` / `FLOOR` | 返回一个数上取整/下取整的结果 |
| `CONV` | 将一个数从一种进制转换成另一种进制 |
| `CRC32` | 计算循环冗余校验码 |
| `EXP` / `LOG` / `LOG2` / `LOG10` | 计算指数/对数 |
| `POW` | 求幂 |
| `RAND` | 返回[0,1)范围的随机数 |
| `ROUND` | 返回一个数四舍五入后的结果 |
| `SQRT` | 返回一个数的平方根 |
| `TRUNCATE` | 截断一个数到指定的精度 |
| `SIN` / `COS` / `TAN` / `COT` / `ASIN` / `ACOS` / `ATAN` | 三角函数 |
常用时间日期函数。
| 函数 | 功能 |
| ----------------------- | ------------------------------------- |
| CURDATE / CURTIME / NOW | 获取当前日期/时间/日期和时间 |
| ADDDATE / SUBDATE | 将两个日期表达式相加/相减并返回结果 |
| DATE / TIME | 从字符串中获取日期/时间 |
| YEAR / MONTH / DAY | 从日期中获取年/月/日 |
| HOUR / MINUTE / SECOND | 从时间中获取时/分/秒 |
| DATEDIFF / TIMEDIFF | 返回两个时间日期表达式相差多少天/小时 |
| MAKEDATE / MAKETIME | 制造一个日期/时间 |
| ----------------------------- | ------------------------------------- |
| `CURDATE` / `CURTIME` / `NOW` | 获取当前日期/时间/日期和时间 |
| `ADDDATE` / `SUBDATE` | 将两个日期表达式相加/相减并返回结果 |
| `DATE` / `TIME` | 从字符串中获取日期/时间 |
| `YEAR` / `MONTH` / `DAY` | 从日期中获取年/月/日 |
| `HOUR` / `MINUTE` / `SECOND` | 从时间中获取时/分/秒 |
| `DATEDIFF` / `TIMEDIFF` | 返回两个时间日期表达式相差多少天/小时 |
| `MAKEDATE` / `MAKETIME` | 制造一个日期/时间 |
常用流程函数。
| 函数 | 功能 |
| ------ | ------------------------------------------------ |
| IF | 根据条件是否成立返回不同的值 |
| IFNULL | 如果为NULL则返回指定的值否则就返回本身 |
| NULLIF | 两个表达式相等就返回NULL否则返回第一个表达式的值 |
| -------- | ------------------------------------------------ |
| `IF` | 根据条件是否成立返回不同的值 |
| `IFNULL` | 如果为NULL则返回指定的值否则就返回本身 |
| `NULLIF` | 两个表达式相等就返回NULL否则返回第一个表达式的值 |
其他常用函数。
| 函数 | 功能 |
| ---------------------- | ----------------------------- |
| MD5 / SHA1 / SHA2 | 返回字符串对应的哈希摘要 |
| CHARSET / COLLATION | 返回字符集/校对规则 |
| USER / CURRENT_USER | 返回当前用户 |
| DATABASE | 返回当前数据库名 |
| VERSION | 返回当前数据库版本 |
| FOUND_ROWS / ROW_COUNT | 返回查询到的行数/受影响的行数 |
| LAST_INSERT_ID | 返回最后一个自增主键的值 |
| UUID / UUID_SHORT | 返回全局唯一标识符 |
| -------------------------- | ----------------------------- |
| `MD5` / `SHA1` / `SHA2` | 返回字符串对应的哈希摘要 |
| `CHARSET` / `COLLATION` | 返回字符集/校对规则 |
| `USER` / `CURRENT_USER` | 返回当前用户 |
| `DATABASE` | 返回当前数据库名 |
| `VERSION` | 返回当前数据库版本 |
| `FOUND_ROWS` / `ROW_COUNT` | 返回查询到的行数/受影响的行数 |
| `LAST_INSERT_ID` | 返回最后一个自增主键的值 |
| `UUID` / `UUID_SHORT` | 返回全局唯一标识符 |
#### DCL数据控制语言
@ -921,7 +994,7 @@ grant all privileges on school.* to 'hellokitty'@'%';
revoke insert, delete, update on school.* from 'hellokitty'@'%';
```
> 说明:创建一个可以允许任意主机登录并且具有超级管理员权限的用户在现实中并不是一个明智的决定,因为一旦该账号的口令泄露或者被破解,数据库将会面临灾难级的风险。
> **说明**:创建一个可以允许任意主机登录并且具有超级管理员权限的用户在现实中并不是一个明智的决定,因为一旦该账号的口令泄露或者被破解,数据库将会面临灾难级的风险。
### 索引
@ -1049,12 +1122,12 @@ drop index idx_student_name on tb_student;
我们简单的为大家总结一下索引的设计原则:
1. **最适合**索引的列是出现在**WHERE子句**和连接子句中的列。
2. 索引列的基数越大(取值多重复值少),索引的效果就越好。
2. 索引列的基数越大(取值多重复值少),索引的效果就越好。
3. 使用**前缀索引**可以减少索引占用的空间,内存中可以缓存更多的索引。
4. **索引不是越多越好**,虽然索引加速了读操作(查询),但是写操作(增、删、改)都会变得更慢,因为数据的变化会导致索引的更新,就如同书籍章节的增删需要更新目录一样。
5. 使用 InnoDB 存储引擎时,表的普通索引都会保存主键的值,所以**主键要尽可能选择较短的数据类型**,这样可以有效的减少索引占用的空间,利用提升索引的缓存效果。
最后还有一点需要说明InnoDB使用的B-tree索引数值类型的列除了等值判断时索引会生效之外使用>、<、>=、<=、BETWEEN...AND... 、<>时,索引仍然生效;对于字符串类型的列,如果使用不以通配符开头的模糊查询,索引也是起作用的,但是其他的情况会导致索引失效,这就意味着很有可能会做全表查询。
最后还有一点需要说明InnoDB 使用的 B-tree 索引,数值类型的列除了等值判断时索引会生效之外,使用`>``<``>=``<=``BETWEEN...AND... ``<>`时,索引仍然生效;对于字符串类型的列,如果使用不以通配符开头的模糊查询,索引也是起作用的,但是其他的情况会导致索引失效,这就意味着很有可能会做全表查询。
### 视图
@ -1107,10 +1180,10 @@ select stuname, avgscore from vw_student_score order by avgscore desc;
既然视图是一张虚拟的表,那么视图的中的数据可以更新吗?视图的可更新性要视具体情况而定,以下类型的视图是不能更新的:
1. 使用了聚合函数SUM、MIN、MAX、AVG、COUNT等、DISTINCT、GROUP BY、HAVING、UNION或者UNION ALL的视图。
2. SELECT中包含了子查询的视图。
3. FROM子句中包含了一个不能更新的视图的视图。
4. WHERE子句的子查询引用了FROM子句中的表的视图。
1. 使用了聚合函数(`SUM``MIN``MAX``AVG``COUNT`等)、`DISTINCT``GROUP BY``HAVING``UNION`或者`UNION ALL`的视图。
2. `SELECT`中包含了子查询的视图。
3. `FROM`子句中包含了一个不能更新的视图的视图。
4. `WHERE`子句的子查询引用了`FROM`子句中的表的视图。
删除视图。
@ -1201,7 +1274,7 @@ MySQL从8.0开始支持窗口函数,大多数商业数据库和一些开源数
上面语法中,窗口函数的位置可以放以下两种函数:
1. 专用窗口函数,包括:`rank`、`dense_rank`和`row_number`等。
1. 专用窗口函数,包括:`lead`、`rank`、`dense_rank`和`row_number`等。
2. 聚合函数,包括:`sum`、`avg`、`max`、`min`和`count`等。
> **参考链接**<https://zhuanlan.zhihu.com/p/92654574>
@ -1233,7 +1306,7 @@ MySQL从8.0开始支持窗口函数,大多数商业数据库和一些开源数
- 检查约束check
> **说明**:在MySQL数据库中,检查约束并不起作用。
> **说明**:在 MySQL 8.x 以前,检查约束并不起作用。
#### 数据一致性
@ -1271,11 +1344,13 @@ MySQL从8.0开始支持窗口函数,大多数商业数据库和一些开源数
rollback
```
大家应该能够想到关于MySQL的知识肯定远远不止上面列出的这些比如MySQL的性能优化、管理和维护MySQL的相关工具、MySQL数据的备份和恢复、监控MySQL、部署高可用架构等问题我们在这里都没有进行讨论。当然这些内容也都是跟项目开发密切相关的我们就留到有需要的时候再进行讲解。
### 总结
关于 MySQL 的知识肯定远远不止上面列出的这些,比如 MySQL 的性能优化、管理和维护 MySQL 的相关工具、MySQL 数据的备份和恢复、监控 MySQL、部署高可用架构等问题我们在这里都没有进行讨论。当然这些内容也都是跟项目开发密切相关的我们就留到有需要的时候再进行讲解。
### Python数据库编程
我们用如下所示的数据库来演示在Python中如何访问MySQL数据库。
我们用如下所示的 SQL 创建数据库,然后为大家演示在 Python 中如何访问 MySQL 数据库。
```SQL
drop database if exists hrs;

View File

@ -0,0 +1,484 @@
## Pandas的应用-4
### DataFrame的应用
#### 数据分析
经过前面的学习,我们已经将数据准备就绪而且变成了我们想要的样子,接下来就是最为重要的数据分析阶段了。当我们拿到一大堆数据的时候,如何从数据中迅速的解读出有价值的信息,这就是数据分析要解决的问题。首先,我们可以获取数据的描述性统计信息,通过描述性统计信息,我们可以了解数据的集中趋势和离散趋势。
例如,我们有如下所示的学生成绩表。
```Python
import numpy as np
import pandas as pd
scores = np.random.randint(50, 101, (5, 3))
names = ('关羽', '张飞', '赵云', '马超', '黄忠')
courses = ('语文', '数学', '英语')
df = pd.DataFrame(data=scores, columns=courses, index=names)
df
```
输出:
```
语文 数学 英语
关羽 96 72 73
张飞 72 70 97
赵云 74 51 79
马超 100 54 54
黄忠 89 100 88
```
我们可以通过`DataFrame`对象的方法`mean`、`max`、`min`、`std`、`var`等方法分别获取每个学生或每门课程的平均分、最高分、最低分、标准差、方差等信息,也可以直接通过`describe`方法直接获取描述性统计信息,代码如下所示。
计算每门课程成绩的平均分。
```Python
df.mean()
```
输出:
```
语文 86.2
数学 69.4
英语 78.2
dtype: float64
```
计算每个学生成绩的平均分。
```Python
df.mean(axis=1)
```
输出:
```
关羽 80.333333
张飞 79.666667
赵云 68.000000
马超 69.333333
黄忠 92.333333
dtype: float64
```
计算每门课程成绩的方差。
```Python
df.var()
```
输出:
```
语文 161.2
数学 379.8
英语 265.7
dtype: float64
```
> **说明**:通过方差可以看出,数学成绩波动最大,最不稳定。
获取每门课程的描述性统计信息。
```Python
df.describe()
```
输出:
```
语文 数学 英语
count 5.000000 5.000000 5.000000
mean 86.200000 69.400000 78.200000
std 12.696456 19.488458 16.300307
min 72.000000 51.000000 54.000000
25% 74.000000 54.000000 73.000000
50% 89.000000 70.000000 79.000000
75% 96.000000 72.000000 88.000000
max 100.000000 100.000000 97.000000
```
##### 排序和Top-N
如果需要对数据进行排序,可以使用`DataFrame`对象的`sort_values`方法,该方法的`by`参数可以指定根据哪个列或哪些列进行排序,而`ascending`参数可以指定升序或是降序。例如,下面的代码展示了如何将学生表按语文成绩排降序。
```Python
df.sort_values(by='语文', ascending=False)
```
输出:
```
语文 数学 英语
马超 100 54 54
关羽 96 72 73
黄忠 89 100 88
赵云 74 51 79
张飞 72 70 97
```
如果`DataFrame`数据量很大排序将是一个非常耗费时间的操作。有的时候我们只需要获得排前N名或后N名的数据这个时候其实没有必要对整个数据进行排序而是直接利用堆结构找出Top-N的数据。`DataFrame`的`nlargest`和`nsmallest`方法就提供对Top-N操作的支持代码如下所示。
找出语文成绩前3名的学生信息。
```Python
df.nlargest(3, '语文')
```
输出:
```
语文 数学 英语
马超 100 54 54
关羽 96 72 73
黄忠 89 100 88
```
找出数学成绩最低的3名学生的信息。
```Python
df.nsmallest(3, '数学')
```
输出:
```
语文 数学 英语
赵云 74 51 79
马超 100 54 54
张飞 72 70 97
```
##### 分组聚合操作
我们先从 Excel 文件中读取一组销售数据,然后再为大家演示如何进行分组聚合操作。
```Python
df = pd.read_excel('2020年销售数据.xlsx')
df.head()
```
> **说明**:如果需要上面例子中的 Excel 文件可以通过下面的阿里云盘地址进行获取该文件在“我的分享”下面的“数据集”目录中。地址https://www.aliyundrive.com/s/oPi7DRAVKRm。
输出:
```
销售日期 销售区域 销售渠道 销售订单 品牌 售价 销售数量
0 2020-01-01 上海 拼多多 182894-455 八匹马 99 83
1 2020-01-01 上海 抖音 205635-402 八匹马 219 29
2 2020-01-01 上海 天猫 205654-021 八匹马 169 85
3 2020-01-01 上海 天猫 205654-519 八匹马 169 14
4 2020-01-01 上海 天猫 377781-010 皮皮虾 249 61
```
如果我们要统计每个销售区域的销售总额,可以先通过“售价”和“销售数量”计算出销售额,为`DataFrame`添加一个列,代码如下所示。
```Python
df['销售额'] = df['售价'] * df['销售数量']
df.head()
```
输出:
```
销售日期 销售区域 销售渠道 销售订单 品牌 售价 销售数量 销售额
0 2020-01-01 上海 拼多多 182894-455 八匹马 99 83 8217
1 2020-01-01 上海 抖音 205635-402 八匹马 219 29 6351
2 2020-01-01 上海 天猫 205654-021 八匹马 169 85 14365
3 2020-01-01 上海 天猫 205654-519 八匹马 169 14 2366
4 2020-01-01 上海 天猫 377781-010 皮皮虾 249 61 15189
```
然后再根据“销售区域”列对数据进行分组,这里我们使用的是`DataFrame`对象的`groupby`方法。分组之后,我们取“销售额”这个列在分组内进行求和处理,代码和结果如下所示。
```Python
df.groupby('销售区域').销售额.sum()
```
输出:
```
销售区域
上海 11610489
北京 12477717
南京 1767301
安徽 895463
广东 1617949
江苏 537079
浙江 687862
福建 10178227
Name: 销售额, dtype: int64
```
如果我们要统计每个月的销售总额我们可以将“销售日期”作为groupby`方法的参数,当然这里需要先将“销售日期”处理成月,代码和结果如下所示。
```Python
df.groupby(df['销售日期'].dt.month).销售额.sum()
```
输出:
```
销售日期
1 5409855
2 4608455
3 4164972
4 3996770
5 3239005
6 2817936
7 3501304
8 2948189
9 2632960
10 2375385
11 2385283
12 1691973
Name: 销售额, dtype: int64
```
接下来我们将难度升级,统计每个销售区域每个月的销售总额,这又该如何处理呢?事实上,`groupby`方法的第一个参数可以是一个列表,列表中可以指定多个分组的依据,大家看看下面的代码和输出结果就明白了。
```Python
df.groupby(['销售区域', df['销售日期'].dt.month]).销售额.sum()
```
输出:
```
销售区域 销售日期
上海 1 1679125
2 1689527
3 1061193
4 1082187
5 841199
6 785404
7 863906
8 734937
9 1107693
10 412108
11 825169
12 528041
北京 1 1878234
2 1807787
3 1360666
4 1205989
5 807300
6 1216432
7 1219083
8 645727
9 390077
10 671608
11 678668
12 596146
南京 7 841032
10 710962
12 215307
安徽 4 341308
5 554155
广东 3 388180
8 469390
9 365191
11 395188
江苏 4 537079
浙江 3 248354
8 439508
福建 1 1852496
2 1111141
3 1106579
4 830207
5 1036351
6 816100
7 577283
8 658627
9 769999
10 580707
11 486258
12 352479
Name: 销售额, dtype: int64
```
如果希望统计出每个区域的销售总额以及每个区域单笔金额的最高和最低,我们可以在`DataFrame`或`Series`对象上使用`agg`方法并指定多个聚合函数,代码和结果如下所示。
```Python
df.groupby('销售区域').销售额.agg(['sum', 'max', 'min'])
```
输出:
```
sum max min
销售区域
上海 11610489 116303 948
北京 12477717 133411 690
南京 1767301 87527 1089
安徽 895463 68502 1683
广东 1617949 120807 990
江苏 537079 114312 3383
浙江 687862 90909 3927
福建 10178227 87527 897
```
如果希望自定义聚合后的列的名字,可以使用如下所示的方法。
```Python
df.groupby('销售区域').销售额.agg(销售总额='sum', 单笔最高='max', 单笔最低='min')
```
输出:
```
销售总额 单笔最高 单笔最低
销售区域
上海 11610489 116303 948
北京 12477717 133411 690
南京 1767301 87527 1089
安徽 895463 68502 1683
广东 1617949 120807 990
江苏 537079 114312 3383
浙江 687862 90909 3927
福建 10178227 87527 897
```
如果需要对多个列使用不同的聚合函数,例如“统计每个销售区域销售额的平均值以及销售数量的最低值和最高值”,我们可以按照下面的方式来操作。
```Python
df.groupby('销售区域')[['销售额', '销售数量']].agg({
'销售额': 'mean', '销售数量': ['max', 'min']
})
```
输出:
```
销售额 销售数量
mean max min
销售区域
上海 20622.538188 100 10
北京 20125.350000 100 10
南京 22370.898734 100 11
安徽 26337.147059 98 16
广东 32358.980000 98 10
江苏 29837.722222 98 15
浙江 27514.480000 95 20
福建 18306.163669 100 10
```
##### 透视表和交叉表
上面的例子中,“统计每个销售区域每个月的销售总额”会产生一个看起来很长的结果,在实际工作中我们通常把那些行很多列很少的表成为“窄表”,如果我们不想得到这样的一个“窄表”,可以使用`DataFrame`的`pivot_table`方法或者是`pivot_table`函数来生成透视表。透视表的本质就是对数据进行分组聚合操作,**根据 A 列对 B 列进行统计**,如果大家有使用 Excel 的经验,相信对透视表这个概念一定不会陌生。例如,我们要“统计每个销售区域的销售总额”,那么“销售区域”就是我们的 A 列,而“销售额”就是我们的 B 列,在`pivot_table`函数中分别对应`index`和`values`参数,这两个参数都可以是单个列或者多个列。
```Python
pd.pivot_table(df, index='销售区域', values='销售额', aggfunc='sum')
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106180912.png" style="zoom:50%">
> **注意**:上面的结果操作跟之前用`groupby`的方式得到的结果有一些区别,`groupby`操作后,如果对单个列进行聚合,得到的结果是一个`Series`对象,而上面的结果是一个`DataFrame` 对象。
如果要统计每个销售区域每个月的销售总额,也可以使用`pivot_table`函数,代码如下所示。
```Python
pd.pivot_table(df, index=['销售区域', df['销售日期'].dt.month], values='销售额', aggfunc='sum')
```
上面的操作结果是一个`DataFrame`,但也是一个长长的“窄表”,如果希望做成一个行比较少列比较多的“宽表”,可以将`index`参数中的列放到`columns`参数中,代码如下所示。
```Python
pd.pivot_table(
df, index='销售区域', columns=df['销售日期'].dt.month,
values='销售额', aggfunc='sum', fill_value=0
)
```
> **说明**`pivot_table`函数的`fill_value=0`会将空值处理为`0`。
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106104551.png" style="zoom:50%">
使用`pivot_table`函数时,还可以通过添加`margins`和`margins_name`参数对分组聚合的结果做一个汇总,具体的操作和效果如下所示。
```Python
df['月份'] = df['销售日期'].dt.month
pd.pivot_table(
df, index='销售区域', columns='月份',
values='销售额', aggfunc='sum', fill_value=0,
margins=True, margins_name='总计'
)
```
输出:
![image-20211106181707655](https://gitee.com/jackfrued/mypic/raw/master/20211106181707.png)
交叉表就是一种特殊的透视表,它不需要先构造一个`DataFrame`对象,而是直接通过数组或`Series`对象指定两个或多个因素进行运算得到统计结果。例如,我们要统计每个销售区域的销售总额,也可以按照如下所示的方式来完成,我们先准备三组数据。
```Python
sales_area, sales_month, sales_amount = df['销售区域'], df['月份'], df['销售额']
```
使用`crosstab`函数生成交叉表。
```Python
pd.crosstab(
index=sales_area, columns=sales_month, values=sales_amount, aggfunc='sum'
).fillna(0).applymap(int)
```
> **说明**:上面的代码使用了`DataFrame`对象的`fillna`方法将空值处理为0再使用`applymap`方法将数据类型处理成整数。
#### 数据可视化
一图胜千言,我们对数据进行透视的结果,最终要通过图表的方式呈现出来,因为图表具有极强的表现力,能够让我们迅速的解读数据中隐藏的价值。和`Series`一样,`DataFrame`对象提供了`plot`方法来支持绘图,底层仍然是通过`matplotlib`库实现图表的渲染。关于`matplotlib`的内容,我们在下一个章节进行详细的探讨,这里我们只简单的讲解`plot`方法的用法。
例如,我们想通过一张柱状图来比较“每个销售区域的销售总额”,可以直接在透视表上使用`plot`方法生成柱状图。我们先导入`matplotlib.pyplot`模块,通过修改绘图的参数使其支持中文显示。
```Python
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'FZJKai-Z03S'
```
> **说明**:上面的`FZJKai-Z03S`是我电脑上已经安装的一种支持中文的字体的名称,字体的名称可以通过查看用户主目录下`.matplotlib`文件夹下名为`fontlist-v330.json`的文件来获得,而这个文件在执行上面的命令后就会生成。
使用魔法指令配置生成矢量图。
```Python
%config InlineBackend.figure_format = 'svg'
```
绘制“每个销售区域销售总额”的柱状图。
```Python
temp = pd.pivot_table(df, index='销售区域', values='销售额', aggfunc='sum')
temp.plot(figsize=(8, 4), kind='bar')
plt.xticks(rotation=0)
plt.show()
```
> **说明**上面的第3行代码会将横轴刻度上的文字旋转到0度第4行代码会显示图像。
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106195040.png" style="zoom:50%">
如果要绘制饼图,可以修改`plot`方法的`kind`参数为`pie`,然后使用定制饼图的参数对图表加以定制,代码如下所示。
```Python
temp.sort_values(by='销售额', ascending=False).plot(
figsize=(6, 6), kind='pie', y='销售额',
autopct='%.2f%%', pctdistance=0.8,
wedgeprops=dict(linewidth=1, width=0.35)
)
plt.legend(loc='center')
plt.show()
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106201550.png" style="zoom:50%">

View File

@ -0,0 +1,2 @@
## 数据分析方法论

View File

@ -0,0 +1,2 @@
## PyTorch入门

View File

@ -1,2 +0,0 @@
## Tensorflow入门

View File

@ -0,0 +1,2 @@
## PyTorch实战

View File

@ -1,2 +0,0 @@
## Tensorflow实战

View File

@ -398,9 +398,9 @@ Python在以下领域都有用武之地。
#### Day88 - [深度学习入门](./Day81-90/88.深度学习入门.md)
#### Day89 - [Tensorflow概述](./Day81-90/89.Tensorflow概述.md)
#### Day89 - [PyTorch概述](./Day81-90/89.PyTorch概述.md)
#### Day90 - [Tensorflow实战](./Day81-90/90.Tensorflow实战.md)
#### Day90 - [PyTorch实战](./Day81-90/90.PyTorch实战.md)
### Day91~100 - [团队项目开发](./Day91-100)

View File

@ -1,24 +1,24 @@
## Zen of PythonPython之禅
1. Beautiful is better than ugly. (优美比丑陋好)
2. Explicit is better than implicit.(清晰比晦涩好)
3. Simple is better than complex.(简单比复杂好)
4. Complex is better than complicated.(复杂比错综复杂好)
5. Flat is better than nested.(扁平比嵌套好)
6. Sparse is better than dense.(稀疏比密集好)
7. Readability counts.(可读性很重要)
8. Special cases aren't special enough to break the rules.(特殊情况也不应该违反这些规则)
9. Although practicality beats purity.(但现实往往并不那么完美)
10. Errors should never pass silently.(异常不应该被静默处理)
11. Unless explicitly silenced.(除非你希望如此)
12. In the face of ambiguity, refuse the temptation to guess.(遇到模棱两可的地方,不要胡乱猜测)
13. There should be one-- and preferably only one --obvious way to do it.(肯定有一种通常也是唯一一种最佳的解决方案)
14. Although that way may not be obvious at first unless you're Dutch.(虽然这种方案并不是显而易见的,因为你不是那个荷兰人[^1]
15. Now is better than never.(现在开始做比不做好)
16. Although never is often better than \*right\* now.(不做比盲目去做好[^2]
17. If the implementation is hard to explain, it's a bad idea.(如果一个实现方案难于理解,它通常不是一个好的方案)
18. If the implementation is easy to explain, it may be a good idea.(如果一个实现方案易于理解,它很有可能是一个好的方案)
19. Namespaces are one honking great idea -- let's do more of those!(命名空间非常有用,我们应当多加利用)
1. Beautiful is better than ugly. (优美比丑陋好)<br>
2. Explicit is better than implicit.(清晰比晦涩好)<br>
3. Simple is better than complex.(简单比复杂好)<br>
4. Complex is better than complicated.(复杂比错综复杂好)<br>
5. Flat is better than nested.(扁平比嵌套好)<br>
6. Sparse is better than dense.(稀疏比密集好)<br>
7. Readability counts.(可读性很重要)<br>
8. Special cases aren't special enough to break the rules.(特殊情况也不应该违反这些规则)<br>
9. Although practicality beats purity.(但现实往往并不那么完美)<br>
10. Errors should never pass silently.(异常不应该被静默处理)<br>
11. Unless explicitly silenced.(除非你希望如此)<br>
12. In the face of ambiguity, refuse the temptation to guess.(遇到模棱两可的地方,不要胡乱猜测)<br>
13. There should be one-- and preferably only one --obvious way to do it.(肯定有一种通常也是唯一一种最佳的解决方案)<br>
14. Although that way may not be obvious at first unless you're Dutch.(虽然这种方案并不是显而易见的,因为你不是那个荷兰人[^1]<br>
15. Now is better than never.(现在开始做比不做好)<br>
16. Although never is often better than \*right\* now.(不做比盲目去做好[^2]<br>
17. If the implementation is hard to explain, it's a bad idea.(如果一个实现方案难于理解,它通常不是一个好的方案)<br>
18. If the implementation is easy to explain, it may be a good idea.(如果一个实现方案易于理解,它很有可能是一个好的方案)<br>
19. Namespaces are one honking great idea -- let's do more of those!(命名空间非常有用,我们应当多加利用)<br>
[^1]:这里指的是 Python 之父 Guido van Rossumm。
[^2]:极限编程中的YAGNI原则

View File

@ -26,9 +26,9 @@
1. I: Can you tell me a little bit about yourself? (介绍下自己)
原则:不要谈私生活和奇怪的癖好(英雄联盟干到钻石因为别人更想知道的是你的专业技能qulifications和工作经验experience所以重点在你之前的公司company name、职位title、时间years和主要职责major responsibilities
原则:不要谈私生活和奇怪的癖好(王者荣耀打到星耀并不值得在这里说因为别人更想知道的是你的专业技能qulifications和工作经验experience所以重点在你之前的公司company name、职位title、时间years和主要职责major responsibilities
C: Thank you for having me. My name is Dachui WANG. I'm 25 years old, and I'm single. I have a Bachelor's Degree of Computer Science from Tsinghua University. I was a Junior Java Programmer for ABC Technologies during my college life. Then I become an intermediate Java engineer for XYZ Corporation in last two years. Programming is my everyday life and programming is where my passion is. I think I have a good knowledge of Java enterprise application developement using light-weight frameworks like Spring, Guice, Hibernate and other open source middle-ware like Dubbo, Mycat, rocketmq and so on and so forth. I love reading, travelling and playing basketball in my spare time. That's all! Thank you!
C: Thank you for having me. My name is Dachui WANG. I'm 22 years old, and I'm single. I have a Bachelor's Degree of Computer Science from Tsinghua University. I was a Junior Java Programmer for ABC Technologies during my college life. Then I become an intermediate Java engineer for XYZ Corporation in last two years. Programming is my everyday life and programming is where my passion is. I think I have a good knowledge of Java enterprise application developement using light-weight frameworks like Spring, Guice, Hibernate and other open source middle-ware like Dubbo, Mycat, rocketmq and so on and so forth. I love reading, travelling and playing basketball in my spare time. That's all! Thank you!
2. I: How would you describe your personality? (你的性格)