MySQL学习代码


MySQL学习代码

SQL

全称 Structured Query Language, 结构化查询语言。操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准。

SQL通用语法

在学习具体的SQL语句之前,先来了解一下SQL语言的通用语法。

  1. SQL语句可以单行或多行书写,以分号结尾。
  2. SQL语句可以使用空格/缩进来增强语句的可读性。
  3. MySQL数据库的SQL语句不区分大小写,关键字建议使用大写。
  4. 注释:
    单行注释:-- 注释内容 或 # 注释内容
    多行注释:/** 注释内容* */

SQL分类

SQL 语句,根据其功能,主要分为四类:DDL、DML、DQL、DCL 。

分类 全称 说明
DDL Data Definition Language 数据定义语言,用来定义数据库对象(数据库,表,字段)
DML Data Manipulation Language 数据操作语言,用来对数据库表中的数据进行增删改
DQL Data Query Language 数据查询语言,用来查询数据库中表的记录
DCL Data Control Language 数据控制语言,用来创建数据库用户、控制数据库的访问权限

MySQL数据库基本操作—DDL

DDL:(Data Definition Language),数据定义语言,该语言部分包括以下内容:

  1. 对数据库的常用操作
  2. 对表结构的常用操作
  3. 修改表结构

数据库操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 查看所有的数据库(结尾一定要加英文[;],表示语句结束)
show databases;
-- 创建数据库(其中 [if not exists] 为可选参数,表示如果不存在再创建,存在不会创建;[charset=utf8] 为编码方式,可选)
create database [if not exists] mydb1 [charset=utf8];
-- 切换(选择要操作的)数据库(mydb1为数据库名)
use mydb1;
-- 删除数据库([if exists] 表示如果有再删除,可选)
drop database [if exists] mydb1;
-- 修改数据库编码(不常用)
alter database mydb1 character set utf8;





表创建—格式

创建表是构建一张空表,指定这个表的名字,这个表有几列,每一列叫什么名字,以及每一列存储的数据类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 创建表格式
create table [if not exists] 表名 (
字段名1 类型[(宽度)] [约束条件] [comment '字段说明'],
字段名2 类型[(宽度)] [约束条件] [comment '字段说明'],
字段名3 类型[(宽度)] [约束条件] [comment '字段说明']
)[表的一些设置];

-- 列如:
-- 选择表(数据库)
use mydb1;
-- 创建表
create table if not exists student (
sid int,
name varchar(20),
gender varchar(10),
age int,
birth date,
address varchar(30),
score double
);



数据类型—数值类型(数字类型)

1、数据类型

数据类型是指在创建表的时候为表中字段指定数据类型,只有数据符合类型要求才能存储起来,使用数据类型的原则是够用就行,尽量使用取值范围小的,而不用大的,这样可以更多的节省存储空间.

  1. 数值类型

数值类型

数据类型—字符串类型

字符串类型

数据类型—日期和时间类型

日期类型

还有 null 类型

  • 没有值,未知
  • 不要使用NULL值进行计算

数据库的字段属性

UnSigned

  • 没有值,未知
  • 不要使用NULL值进行计算

ZEROFILL

  • 0填充的
  • 不足位数的用0来填充 , 如int(3),5则为005

Auto_InCrement

  • 通常理解为自增,自动在上一条记录的基础上默认+1
  • 通常用来设计唯一的主键,必须是整数类型
  • 可定义起始值和步长
      • 当前表设置步长(AUTO_INCREMENT=100) : 只影响当前表
      • SET @@auto_increment_increment=5 ; 影响所有使用自增的表(全局)

NULL 和 NOT NULL

  • 默认为NULL , 即没有插入该列的数值
  • 如果设置为NOT NULL , 则该列必须有值

DEFAULT

  • 默认的
  • 用于设置默认值
  • 例如,性别字段,默认为"男" , 否则为 “女” ; 若无指定该列的值 , 则默认值为"男"的值
1
2
3
4
5
6
7
8
9
10
11
12
-- 例子:执行 desc 表名; 后会出现以下结果
CREATE TABLE IF NOT EXISTS `student`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
`sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别',
`birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

表的其他操作

1
2
3
4
5
6
7
8
9
10
-- 查看当前数据库的所有表名称
show tables;
-- 查看指定某个表的创建语句
show create table 表名;
-- 查看表结构
desc 表名;
-- 删除表
drop table 表名;


修改表结构

修改表添加列(字段)

1
2
3
4
5
6
-- 为表添加新列(字段)
alter table 表名 add 列名 类型(长度) [约束];
-- 例子:为student表添加一个新的字段为:系别 dept 类型为 varchar(20)
alter table student add dept varchar(20);


修改列名和类型

1
2
3
4
5
6
7
8
9
10
11
-- 语法格式
alter table 表名 change 旧列名 新列名 类型(长度) [约束];
-- 例子:为student表的 dept 字段更换为 department varchar(30)
alter table student change dept department varchar(30);

-- 修改列的数据类型
alter table 表名 modify column 列名 新的列的类型
-- 例子:为student表的department列的类型修改为char(20)
alter table student modify column department char(20);


修改表删除列

1
2
3
4
5
6
-- 语法格式
alter table 表名 drop 列名;
-- 例如:删除student表中department这列
alter table student drop department;


修改表名

1
2
3
4
5
6
-- 语法格式
rename table 表名 to 新表名;
-- 例子:将表student改名成stu
rename table student to stu;


MySQL数据库基本操作—DML

DML基本介绍

DML是指数据操作语言,英文全称是Data Manipulation Language,用来对数据库中表的数据记录进行更新(增删改)。

关键字:

  • 插入insert
  • 删除delete
  • 更新update

数据插入

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 语法格式
-- 向表中插入某些(列和值要相互对应)
insert into 表(列名1,列名2,列名3...) values(值1,值2,值3...);
-- 向表中插入所有列
insert intovalues(值1,值2,值3...);

-- 例子:
insert into student(sid,name,gender,age,birth,address,score) values(1001,'张三','男',18,'1996-12-23','北京',83.5);
insert into student values(1001,'张三','男',18,'1996-12-23','北京',83.5);
-- 一次添加多行数据
insert into student values(1001,'张三','男',18,'1996-12-23','北京',83.5),
(1002,'李四','男',19,'1997-12-23','北京',85.5);

数据修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 语法格式
update 表名 set 字段名 = 值,字段名 = 值...;
update 表名 set 字段名 = 值,字段名 = 值... where 条件;

-- 例子:
-- 将所有学生的地址修改为重庆
update student set address = '重庆';
-- 将sid为1004的学生的地址修改为北京
update student set address = '北京' where sid = 1004;
-- 将sid大于1004的学生的地址修改为上海
update student set address = '北京' where sid > 1004;
-- 将sid为1005的学生的地址修改为北京,成绩修改为100
update student set address = '北京',score = 100 where sid = 1005;


数据删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 语法格式(delete 不加条件会清空该表所有数据)
delete from 表名 [where 条件];
truncate table 表名 或者 truncate 表名;

-- 例子
-- 1.删除sid为1004的学生数据
delete from student where sid = 1004;
-- 2.删除表所有数据
delete from student;
-- 3.清空表数据
-- 方式一
truncate table student;
-- 方式二
truncate student;

-- 注意:delete 和 truncate 原理不同,delete 只删除内容,而 truncate 类似于 drop table,可以理解为是将整个表删除,然后再创建该表;

MySQL约束

简介

概念:

约束英文:constraint

约束实际上就是表中数据的限制条件。

作用

表在设计的时候加入约束的目的就是为了保证表中的记录完整性和有效性,比如用户表有些列的值(手机号)不能为空,有些列的值(身份证号)不能重复。

分类

  • 主键约束(primary key) PK
  • 自增长约束(auto_increment)
  • 非空约束(not null)
  • 唯一性约束(unique)
  • 默认约束(default)
  • 零填充约束(zerofill)
  • 外键约束(foreign key) FK
  • 检查约束(8.0.16版本之后) (check)

主键约束*

概念:

  • MySQL主键约束是一个列或者多个列的组合,其值能唯一地标识表中的每一行,方便在RDBMS中尽快的找到某一行。
  • 主键约束相当于唯一约束+非空约束的组合,主键约束列不允许重复,也不允许出现空值。
  • 每个表最多只允许一个主键。
  • 主键约束的关键字是:primary key
  • 当创建主键的约束时,系统默认会在所在的列和列组合上建立对应的唯一索引。

操作

  • 添加单列主键

创建单列主键有两种方式,一种是在定义字段的同时指定主键,一种是定义完字段之后指定主键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- 方式1——语法:
-- 在 create table 语句中,通过 primary key 关键字来指定主键。
-- 在定义字段的同时指定主键,语法格式如下:
create table 表名 (
...
<字段名> <数据类型> primary key,
...
);
-- 方式1——实现:
create table emp1 (
eid int primay key,
name varchar(20),
deptId int,
salary double
);

-- 方式2——语法:
-- 在定义字段的同时指定主键,语法格式如下:
create table 表名 (
...
[constraint <约束名>] primary key [字段名]
);
-- 方式2——实现:
create table emp2 (
eid int,
name varchar(20),
deptId int,
salary double,
constraint pk1 primary key(eid)
);



  • 添加多列联合主键

所谓的联合主键,就是这个主键是由一张表中多个字段组成的。

注意:

	1. 当主键是由多个字段组成时,不能直接在字段名后面声明主键约束。
	2. 一张表只能有一个主键,联合主键也是一个主键。
	3. 联合主键各列不能为空。
1
2
3
4
5
6
7
8
9
10
11
12
13
-- 语法:
create table 表名 (
...
primary key(字段1,字段2,...,字段n)
);
-- 实现:
create table emp3 (
name varchar(20),
deptId int,
salary double,
primary key(name, deptId)
);

  • 通过修改表结构添加主键

    主键约束不仅可以在创建表的同时创建,也可以在修改表时添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 语法:
create table 表名 (
...
);
alter table <表名> add primary key (字段列表);

-- 实现:
-- 添加单列主键
create table emp4 (
eid int,
name varchar(20),
deptId int,
salary double
);
alter table emp4 add primary key (eid);

-- 添加多列主键(联合)
create table emp5 (
eid int,
name varchar(20),
deptId int,
salary double
);
alter table emp5 add primary key (eid, deptId);

  • 删除主键约束

一个表中不需要主键约束时,就需要从表中将其删除。删除主键约束的方法要比创建主键约束容易的多。

1
2
3
4
5
6
7
8
9
10
-- 格式
alter table <数据库名> drop primary key;

-- 实现
-- 删除单列主键
alter table emp1 drop primary key;

-- 删除联合主键
alter table emp5 drop primary key;

自增长约束(auto_increment)

概念:

在MySQL中,当主键定义为自增长后,这个主键的值就不再需要用户输入数据了,而由数据库系统根据定义自动赋值。每增加一条记录,主键会自动以相同的步长进行增长。

通过给字段添加 auto_increment 属性来实现主键自增长。

一般搭配主键使用,提高健壮性。

1
2
3
4
5
6
7
8
9
-- 语法
字段名 数据类型 auto_increment;

-- 操作
create table t_user1 (
id int primary key auto_increment,
name varchar(20)
);

  • 特点:

    1. 默认情况下,auto_increment的初始值是 1,每新增一条记录,字段值自动加 1。

    2. 一个表中只能有一个字段使用 auto_increment约束,且该字段必须有唯一索引,以避免序号重复(即为主键或主键的一部分)。

    3. auto_increment约束的字段必须具备 NOT NULL 属性。

    4. auto_increment约束的字段只能是整数类型(TINYINT、SMALLINT、INT、BIGINT )等。

    5. auto_increment约束字段的最大值受该字段的数据类型约束,如果达到上限,auto_increment就会失效。

指定自增字段初始值

  1. 如果第一条记录设置了该字段的初始值,那么新增加的记录就从这个初始值开始自增。例如,如果表中插入的第一条记录的id值设置为5,那么再插入记录时,id值就会从5开始往上增加。
1
2
3
4
5
6
7
8
9
10
11
12
13
-- 方式1,创建表时指定
create table t_user2 (
id int primary key auto_increment,
name varchar(20)
)auto_increment = 100;

-- 方式2,创建表之后指定
create table t_user2 (
id int primary key auto_increment,
name varchar(20)
);
alter table t_user2 auto_increment = 200;

注意:delete 和 truncate 在删除后自增列的变化:

  • delete数据之后自动增长从断点开始
  • truncate数据之后自动增长从默认起始值开始
1
2
3
4
5
6
7
8
-- 格式
delete from <表名>;
truncate <表名>;

-- 实现
delete from t_user1;
truncate t_user2;

非空约束(not null)

概念

MySQL非空约束(not null)指字段的值不能为空。对于使用了非空约束的字段,如果用户在添加数据时没有指定值,数据库系统就会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
-- 语法:
-- 方式1:
<字段名> <数据类型> not null;
-- 方式2:
alter table 表名 modify 字段 类型 not null;

-- 添加非空约束——方式1
-- 方式1,创建表时指定
create table t_user6 (
id int,
name varchar(20) not null,
address varchar(20) not null
);

-- 添加非空约束——方式2
-- 方式2,创建表之后指定
create table t_user7 (
id int,
name varchar(20),
address varchar(20)
);
alter table t_user7 modify name varchar(20) not null;
alter table t_user7 modify address varchar(20) not null;
-- 实例(插入数据):
insert into t_user6(id) values(1001); -- 不可以(name,address不能为空)
insert into t_user6(id,name,address) values(1001,NULL,NULL); -- 不可以(同上)
insert into t_user6(id,name,address) values(1001,'NULL','NULL'); -- 可以(字符串NULL)
insert into t_user6(id,name,address) values(1001,'',''); -- 可以(空串)


-- 删除非空约束(可以叫修改)
-- alter table 表名 modify 字段 类型;
alter table t_user7 modify name varchar(20);
alter table t_user7 modify address varchar(20);


唯一约束(unique)

概念

唯一约束(Unique), 是指所有记录中字段的值不能重复出现。例如,为 id 字段加上唯一性约束后,每条记录的 id 值都是唯一的,不能出现重复的情况,但可以为NULL,并且可以有多个NULL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 语法:
-- 方式1:
<字段名> <数据类型> unique;
-- 方式2:
alter table 表名 add constraint 约束名 unique(列);

-- 添加唯一约束——方式1:
-- 创建表时指定
create table t_user8 (
id int,
name varchar(20),
phone_number varchar(20) unique -- 指定唯一约束
);

-- 添加唯一约束——方式2:
-- 方式2,创建表之后指定
create table t_user9 (
id int,
name varchar(20),
phone_number varchar(20) -- 指定唯一约束
);
alter table t_user9 add constraint unique_pn unique(phone_number);

-- 删除唯一约束
alter table <表名> drop index <唯一约束名>;
-- 实例:(如果通过方式1添加的唯一约束,则index后面加上所在列名就行)
alter table t_user9 drop index unique_pn;

默认约束(default)

概念

MySQL默认值约束用来指定某列的默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
-- 语法:
-- 方式1:
<字段名> <数据类型> default <默认值>;
-- 方式2:
alter table 表名 modify 列名 类型 default 默认值;

-- 添加默认约束——方式1:
-- 创建表时指定
create table t_user10 (
id int,
name varchar(20),
address varchar(20) default '北京' -- 指定默认约束
);
-- 插入数据(未给定地址时,它会默认是北京)
insert into t_user10(id,name) values(1001,'张三');
insert into t_user10(id,name) values(1002,'李四','上海'); -- 地址会成为上海
insert into t_user10 values(1003,'王五',NULL); -- 地址会为 NULL

-- 添加默认约束——方式2:
-- 创建表之后指定
create table t_user11 (
id int,
name varchar(20),
address varchar(20)
);
-- 格式:alter table 表名 modify 列名 类型 default 默认值;
alter table t_user11 modify address varchar(20) default '北京';

-- 删除默认约束(删除默认约束就是把设置的默认值设置为null)
-- alter table <表名> change column <字段名> <类型> default NULL;
alter table t_user11 modify address varchar(20) default NULL;

零填充约束(zerofill)

概念

  1. 插入数据时,当该字段的值的长度小于定义的长度时,会在该值的前面补上相应的0;
  2. zerofill 默认为 int(10);
  3. 当使用 zerofill 时,默认会自动加 unsigned(无符号)属性,使用 unsigned 属性后,数值范围时原值的2倍,例如,有符号为 -128~+127,无符号为 0~256.

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 语法
<字段名> <数据类型> zerofill;

create table t_user12 (
id int zerofill, -- 零填充约束
name varchar(20)
);
insert into t_user12 values(123,'张三'); -- 固定10位,会在前面加7个0
insert into t_user12 values(1,'李四'); -- 固定10位,会在前面加9个0

-- 删除
-- 格式:alter table <表名> modify <字段名> <数据类型>;
alter table t_user12 modify id int;

MySQL数据库基本操作—DQL

基本介绍

概念

  • 数据库管理系统一个重要功能就是数据查询,数据查询不应只是简单返回数据库中存储的数据,还应该根据需要对数据进行筛选以及确定数据以什么样的格式显示。
  • MySQL提供了功能强大、灵活的语句来实现这些操作。
  • MySQL数据库使用select语句来查询数据。

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- 格式
select
[all|distinct]
<目标列的表达式1> [别名],
<目标列的表达式2> [别名]...
from <表名或视图名> [别名], <表名或视图名> [别名]...
[where<条件表达式>]
[group by <列名>
[having <条件表达式>]]
[order by <列名> [asc|desc]]
[limit <数字或者列表>];

-- 解释以上
SELECT
字段列表
FROM
表名列表
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
ORDER BY
排序字段列表
LIMIT
分页参数

-- 简化版语法
select *| 列名 fromwhere 条件;



我们在讲解这部分内容的时候,会将上面的完整语法进行拆分,分为以下几个部分:

  • 基本查询(不带任何条件)
  • 条件查询(WHERE)
  • 聚合函数(count、max、min、avg、sum)
  • 分组查询(group by)
  • 排序查询(order by)
  • 分页查询(limit)

基本查询—数据准备

以下是一个基础例子:

  • 创建数据库和表(商品表):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 创建数据库
    create database if not exists mydb2;
    use mydb2;

    -- 创建商品表:
    create table product (
    pid int primary key auto_increment, -- 商品编号
    pname varchar(20) not null, -- 商品名称
    price double, -- 商品价格
    category_id varchar(20) -- 商品所属分类
    );

  • 添加数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    -- 电器类(c001):
    insert into product values(null,'海尔洗衣机',5000,'c001');
    insert into product values(null,'美的冰箱',3000,'c001');
    insert into product values(null,'格力空调',5000,'c001');
    insert into product values(null,'九阳电饭煲',5000,'c001');

    -- 服装类(c002):
    insert into product values(null,'啄木鸟衬衣',300,'c002');
    insert into product values(null,'恒源祥西裤',800,'c002');
    insert into product values(null,'花花公子夹克',440,'c002');
    insert into product values(null,'劲霸休闲裤',266,'c002');
    insert into product values(null,'海澜之家卫衣',180,'c002');
    insert into product values(null,'杰克琼斯运动裤',430,'c002');

    -- 护肤品类(c003):
    insert into product values(null,'兰蔻面霜',300,'c003');
    insert into product values(null,'雅诗兰黛精华水',200,'c003');
    insert into product values(null,'香奈儿香水',350,'c003');
    insert into product values(null,'SK-II神仙水',350,'c003');
    insert into product values(null,'资生堂粉底液',180,'c003');

    -- 食品类(c004):
    insert into product values(null,'老北京方便面',56,'c004');
    insert into product values(null,'良品铺子海带丝',17,'c004');
    insert into product values(null,'三只松鼠坚果',88,'c004');

基本查询—简单查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 1.查询所有的商品
select * from product;

-- 2.查询商品名和商品价格
select pname,price from product;

-- 3.别名查询.使用的关键字是as(as可以省略的)
-- 3.1表别名:
select * from product as p;
-- 别名在多表查询时可以起到简化作用,例如:
select p.id,u.id from product as p, user1 as u;

-- 3.2列别名(商品名):
select pname as pn from product;

-- 4.去掉重复值(商品价格)
select distinct price from product;

-- 5.查询结果是表达式(运算查询):将商品名和所有商品的价格+10元进行显示
select pname,price+10 from product;

基本查询—运算符

简介

数据库中的表结构确立后,表中的数据代表的意义就已经确定。通过MySQL运算符进行运算,就可以获取到表结构以外的另一种数据。

例如,学生表中存在一个birth字段,这个字段表示学生的出 年份。 运 MySQL的算术运算符用当前的年份减学生出生的年份,那么得到的就是这个学生的实际年龄数据。

MySQL支持4种运算符

  • 算术运算符
  • 比较运算符
  • 逻辑运算符
  • 位运算符

算术运算符

算术运算符 说明
+ 加法运算
- 减法运算
* 乘法运算
/ 或 DIV 除法运算,返回商
% 或 MOD 求余运算,返回余数

比较运算符

比较运算符 说明
= 等于
< 和 <= 小于和小于等于
> 和 >= 大于和大于等于
<=> 完全的等于,两个操作码均为NULL时,其所得值为1;而当一个操作码为NULL时,其所得值为0
<> 或 != 不等于
IS NULL 或 ISNULL 判断一个值是否为NULL
IS NOT NULL 判断一个值是否不为NULL
LEAST 当有两个或多个参数时,返回最小值
GREATEST 当有两个或多个参数时,返回最大值
BETWEEN AND 判断一个值是否落在两个值之间
IN 判断一个值是IN列表中的任意一个值
NOT IN 判断一个值不是IN列表中的任意一个值
LIKE 通配符匹配(包括 % 和 _ ,%表示多个匹配,_ 表示单个匹配)
REGEXP 正则表达式匹配

逻辑运算符

逻辑运算符 说明
NOT 或者 ! 逻辑非
AND 或者 && 逻辑与
OR 或者 || 逻辑或
XOR 逻辑异或

位运算符

位运算符 说明
! 按位或
& 按位与
^ 按位异或
<< 按位左移
>> 按位右移
~ 按位取反,反转所有比特

位运算符是在二进制数上进行计算的运算符。位运算会先将操作数变成二进制数,进行位运算。然后再将计算结果从二进制数变回十进制数。

运算符操作

算术运算符
1
2
3
4
5
6
7
8
9
10
11
12
use mydb2;
select 6 + 2;
select 6 - 2;
select 6 * 2;
select 6 / 2;
select 6 % 2;

-- 将每件商品的价格加10
select name,price + 10 as new_price from product;
-- 将所有商品的价格上调10%
select pname,price * 1.1 as new_price from product;

比较运算符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
-- 更新pid为18的category_id为null
update product set category_id = null where pid = 18;


-- 比较运算符——条件查询

-- 查询商品名称为“海尔洗衣机”的商品所有信息:
select * from product where pname = '海尔洗衣机';

-- 查询价格为800的商品
select * from product where price = 800;

-- 查询价格不是800的所有商品(三种方法)
select * from product where price != 800;
select * from product where price <> 800;
select * from product where not(price = 800);

-- 查询商品价格大于60元的所有商品信息
select * from product where price > 60;

-- 查询商品价格在200到1000之间所有商品(三种方法)
select * from product where price >= 200 and price <= 1000;
select * from product where price >= 200 && price <= 1000;
select * from product where price between 200 and 1000;

-- 查询商品价格是200或800的所有商品(三种方法)
select * from product where price = 200 or price = 800;
select * from product where price = 200 || price = 800;
select * from product where price in(200, 800);

-- 查询含有‘裤'字的所有商品
select * from product where pname like '%裤%';

-- 查询以'海'开头的所有商品
select * from product where pname like '海%';

-- 查询第二个字为'蔻'的所有商品
select * from product where pname like '_蔻%';

-- 查询category_id为null的商品
select * from product where category_id is null;

-- 查询category_id不为null分类的商品
select * from product where category_id is not null;

-- 使用least求最小值(可以结合表来查询)
select least(10, 20, 30) as small_number; -- 10
select least(10, null , 30); -- null
 
-- 使用greatest求最大值
select greatest(10, 20, 30) as big_number; -- 30
select greatest(10, null, 30); -- null

位运算符(了解)

位运算符是在二进制数上进行计算的运算符。位运算会先将操作数变成二进制数,进行位运算。然后再将计算结果从二进制数变回十进制数。

1
2
3
4
5
6
7
select 3 & 5;  -- 位与
select 3 | 5; -- 位或
select 3 ^ 5; -- 位异或
select 3 >> 1; -- 位右移
select 3 << 1; -- 位左移
select ~ 3; -- 位取反

基本查询—排序查询

介绍

如果我们需要对读取的数据进行排序,我们就可以使用MySQL的 order by 子句来设定你想按哪个字段哪种方式来进行排序,再返回搜索结果。

1
2
3
4
5
6
7
8
-- 语法格式:
select
字段名1, 字段名2, ……
from 表名
order by 字段名1 [asc|desc], 字段名2[asc|desc] ……;



特点

  1. asc代表升序,desc代表降序,如果不写默认升序;
  2. order by用于子句中可以支持单个字段,多个字段,表达式,函数,别名;
  3. order by子句,放在查询语句的最后面。LIMIT子句除外。

操作

1
2
3
4
5
6
7
8
9
10
-- 1.使用价格排序(降序)
select * from product order by price desc;

-- 2.在价格排序(降序)的基础上,以分类排序(降序)
select * from product order by price desc, category_id desc;

-- 3.显示商品的价格(去重复),并排序(降序)
select distinct price from product order by price desc;


基本查询—聚合查询

简介

之前我们做的查询都是横向查询,它们都是根据条件一行一行的进行判断,而使用聚合函数查询是纵向查询,它是对一列的值进行计算,然后返回一个单一的值;另外聚合函数会忽略空值。

聚合函数 作用
count() 统计指定列不为NULL的记录行数;
sum() 计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
max() 计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算;
min() 计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算;
avg() 计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 1 查询商品的总条数
select count(*) from product;

-- 2 查询价格大于200商品的总条数
select count(*) from product where price > 200;

-- 3 查询分类为'c001'的所有商品的总和
select sum(price) from product where category_id = 'c001';

-- 4 查询商品的最大价格
select max(price) from product;

-- 5 查询商品的最小价格
select min(price) from product;

-- 6 查询分类为'c002'所有商品的平均价格
select avg(price) from product where category_id = 'c002';

-- 7 查询商品的最大和最小价格,并分别命名为 max_price 和 min_price :
select max(price) max_price, min(price) min_price from product;

NULL值的处理

介绍
  1. count函数对null值的处理:
    如果count函数的参数为星号(*),则统计所有记录的个数。而如果参数为某字段,不统计含null值的记录个数。

  2. sum和avg函数对null值的处理:

    这两个函数忽略null值的存在,就好像该条记录不存在一样。

  3. max和min函数对null值的处理:

    max和min两个函数同样忽略null值的存在。

操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 创建表
create table test_null (
c1 varchar(20),
c2 int
);

-- 插入数据
insert into test_null values('aaa',3);
insert into test_null values('bbb',3);
insert into test_null values('ccc',null);
insert into test_null values('ddd',6);

-- 测试
-- SELECT COUNT(*)和 SELECT COUNT(1) 是一个意思
select count(*), count(1), count(c2) from test_null;
select sum(c2),max(c2),min(c2),avg(c2) from test_null;

基本查询—分组查询—group by

简介

分组查询是指使用group by字句对查询信息进行分组。

1
2
3
4
5
6
7
8
9
10
-- 格式:
select 字段1,字段2…… from 表名 group by 分组字段 having 分组条件;

-- 操作
-- 1、统计各个分类商品的个数(注意:分组之后select的后边只能写分组字段和聚合函数)
select category_id,count(*) from product group by category_id;
-- group by可以指定多个分组字段;假如有三个分组字段,则筛选时必须三个分组字段一致才会分到一起(不分先后)。
-- 例如:学生表,有: 学号 名字 年龄 省 市 县;(分组字段可不分先后)
select from product group by 市, 省, 县; -- 筛查结果为同一个省市县的分到一起并统计个数

如果要进行分组的话,则select子句之后,只能出现分组的字段和统计函数,其他的字段不能出现;

分组之后的条件筛选—having

  • 分组之后对统计结果进行筛选的话必须使用having,不能使用where;
  • where子句用来筛选FROM子句中指定的操作所产生的行;
  • group by 子句用来分组where子句的输出;
  • having 子句用来从分组的结果中筛选行。
1
2
3
4
5
6
7
8
-- 格式
select 字段1, 字段2from 表名 group by 分组字段 having 分组条件;

-- 操作
-- 2.统计各个分类商品的个数,且只显示个数大于4的信息
select category_id, count(*) from product group by category_id having count(*) > 4;


基本查询—分页查询—limit

简介

分页查询在项目开发中常见,由于数据量很大,显示屏长度有限,因此对数据需要采取分页显示方式。例如数据共有30条,每页显示5条,第一页显示1-5条,第二页显示6-10条。

格式

1
2
3
4
5
6
7
8
9
10
-- 方式1——显示前n条
select 字段1, 字段2from 表名 limit n;

-- 方式2——分页显示
select 字段1, 字段2from 表名 limit m,n;

m:整数,表示从第几条索引开始,计算方式(当前页-1*每页显示条数
n:整数,表示查询多少条数据


操作

1
2
3
4
5
6
7
-- 查询product表的前5条记录
select * from product limit 5;

-- 从第4条开始显示,显示5条
select * from product limit 3,5;


基本查询—INSERT INTO SELECT语句

简介

将一张表的数据导入到另一张表中,可以使用INSERT INTO SELECT语句 。

格式

1
2
3
4
5
6
7
insert into Table2(field1, field2) select value1, value2,… from Table1;
或者:
insert into Table2 select * from Table1;

注意:要求目标表Table2必须存在


操作

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 实例
-- 创建表名为product2的表(要与表1中的字段名一致)
create table product2 (
pname varchar(20),
price double
);

-- 从表1将数据插入到表2(pname,price)
insert into product2(pname,price) select pname,price from product;

-- 检查是否成功插入
select * from product2;

SELECT INTO FROM语句

简介

将一张表的数据导入到另一张表中,有两种选择 SELECT INTO 和 INSERT INTO SELECT 。

格式

1
select value1, value2 into Table2 from Table1;

注意: 要求目标表Table2不存在,因为在插入时会自动创建表Table2,并将Table1中指定字段数据复制到Table2中。

操作

1
select pname,price into product3 from product;

练习1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
-- 选择数据库
use mydb2;

-- 创建学生表
create table student (
id int,
name varchar(20),
gender varchar(20),
chinese int,
english int,
math int
);

-- 插入数据
insert into student(id,name,gender,chinese,english,math) values(1,'张明','男',89,78,90);
insert into student(id,name,gender,chinese,english,math) values(2,'李进','男',67,53,95);
insert into student(id,name,gender,chinese,english,math) values(3,'王五','女',87,78,77);
insert into student(id,name,gender,chinese,english,math) values(4,'李一','女',88,98,92);
insert into student(id,name,gender,chinese,english,math) values(5,'李财','男',82,84,67);
insert into student(id,name,gender,chinese,english,math) values(6,'张宝','男',55,85,45);
insert into student(id,name,gender,chinese,english,math) values(7,'黄蓉','女',75,65,30);
insert into student(id,name,gender,chinese,english,math) values(7,'黄蓉','女',75,65,30);

-- 查询数据
-- 1.查询表中所有学生的信息。
select * from student;
-- 2.查询表中所有学生的姓名和对应的英语成绩。
select name,english from student;
-- 3.过滤表中重复数据。
select distinct * from student;
-- 4.统计每个学生的总分。
select name,(chinese + english + math) as total_score from student;
-- 5.在所有学生总分数上加10分特长分。
select name,(chinese + english + math)+10 as total_score from student;
-- 6.使用别名表示学生分数。
select name,chinese '语文成绩', english '英语成绩', math '数学成绩' from student;
-- 7.查询英语成绩大于90分的同学。
select * from student where english > 90;
-- 8.查询总分大于200分的所有同学。
select *,(chinese + english + math) as total_score from student where (chinese + english + math) > 200;
-- 9.查询英语分数在80-90之间的同学。(两种方法)
select * from student where english between 80 and 90;
select * from student where english >= 80 and english <= 90;
-- 10.查询英语成绩不在80-90之间的同学。(四种方法)
select * from student where not (english between 80 and 90);
select * from student where english not between 80 and 90;
select * from student where not (english >= 80 and english <= 90);
select * from student where english < 80 || english > 90;
-- 11.查询数学分数为89,90,91的同学。
select * from student where math in(89,90,91);
-- 12.查询数学分数不为89,90,91的同学。
select * from student where math not in(89,90,91);
select * from student where not math in(89,90,91);
-- 13.查询所有姓李的学生英语成绩。
select name,english from student where name like '李%';
-- 14.查询数学分80并且语文分80的同学。
select * from student where math = 80 and chinese = 80;
-- 15.查询英语80或者总分200的同学。
select * from student where english = 80 or (chinese + english + math) = 200;
-- 16.对数学成绩降序排序后输出。
select * from student order by math desc;
-- 17.对总分排序后输出,然后再按从高到低的顺序输出。
select *,(chinese + english + math) as total_score from student order by (chinese + english + math) desc;
-- 18.对姓李的学生总分成绩降序排序输出。
select *,(chinese + english + math) as total_score from student where name like '李%' order by (chinese + english + math) desc;
-- 19.查询男生和女生分别有多少人,并将人数降序排序输出。
select gender,count(*) as total_count from student group by gender order by total_count desc;
-- 20.查询男生和女生分别有多少人,并将人数降序排序输出,查询出人数大于等于4的性别人数信息。
select gender,count(*) as total_count from student group by gender having total_count >= 4 order by total_count desc;


练习2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
-- 选择mydb2数据库
use mydb2;

-- 创建工资表
create table emp (
empno int, -- 员工编号
ename varchar(50), -- 员工名字
job varchar(50), -- 工作名字
mgr int, -- 上级领导编号
hiredate date, -- 入职日期
sal int, -- 薪资
comm int, -- 奖金
deptno int -- 部门编号
);

-- 插入数据
insert into emp values(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
insert into emp values(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
insert into emp values(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
insert into emp values(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
insert into emp values(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
insert into emp values(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
insert into emp values(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);
insert into emp values(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);
insert into emp values(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
insert into emp values(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
insert into emp values(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);
insert into emp values(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
insert into emp values(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
insert into emp values(7934,'MTLLER','CLERK',7782,'1982-1-23',1300,NULL,10);

-- 查询数据
-- 1、按员工编号升序排列不在10号部门工作的员工信息。(两种方法)
select * from emp where deptno not in(10) order by empno;
select * from emp where deptno != 10 order by empno;
-- 2、查询姓名第二个字母不是“A”且薪水大于1000元的员工信息,按年薪降序排列。
select *,(sal * 12 + ifnull(comm,0)) yearly from emp where ename not like '_A%' and sal > 1000 order by (sal * 12 + ifnull(comm,0)) desc;
-- 3、求每个部门的平均薪水,并按平均薪水降序排序。
select deptno,avg(sal) as avg_sal from emp group by deptno order by avg_sal desc;
-- 4、求各个部门的最高薪水。
select deptno,max(sal) as max_sal from emp group by deptno;
-- 5、求每个部门每个岗位的最高薪水并按部门排序。
select deptno,job,max(sal) from emp group by deptno,job order by deptno;
-- 6、求平均薪水大于2000的部门编号。
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2000;
-- 7、将部门平均薪水大于1500的部门编号列出来,按部门平均薪水降序排列。
select deptno,avg(sal) avg_sal from emp group by deptno having avg(sal) > 1500 order by avg_sal desc;
-- 8、选择公司中有奖金的员工姓名,工资。
select ename,sal,comm from emp where comm is not null;
-- 9、查询员工最高工资和最低工资的差距。
select max(sal) - min(sal) '薪资差距' from emp;



正则表达式

介绍

​ 正则表达式(regular expression)描述了一种字符串匹配的规则,正则表达式本身就是一个字符串,使用这个字符串来描述、用来定义匹配规则,匹配一系列符合某个句法规则的字符串。在开发中,正则表达式通常被用来检索、替换那些符合某个规则的文本。

​ MySQL通过REGEXP关键字支持正则表达式进行字符串匹配。

格式

模式 描述
^ 匹配输入字符串的开始位置。
$ 匹配输入字符串的结束位置。
. 匹配除"\n"之外的任何某个字符。
[…] 字符集合。匹配所包含的任意一个字符。例如,'[abc]‘可以匹配"plain"中的’a’。
[^…] 负值字符集合。匹配未包含的任意字符。例如,'[^abc]‘可以匹配"plain"中的’p’。
p1|p2|p3 匹配p1或p2或p3。例如,‘z|food’能匹配"z"或"food"。’(z|f)ood’则匹配"zood"或"food"。
* 匹配前面的子表达式零次或多次。例如,"zo*“能匹配"z"以及"zoo”。 * 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,“zo+“能匹配"zo"以及"zoo”,但不能匹配"z”。+ 等价于 {1,}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。

正则表达式匹配查询

操作上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- ^ 在字符串开始处进行匹配
SELECT 'abc' REGEXP '^a';
 
-- $ 在字符串末尾开始匹配
SELECT 'abc' REGEXP 'a$';
SELECT 'abc' REGEXP 'c$';

-- . 匹配任意字符
SELECT 'abc' REGEXP '.b';
SELECT 'abc' REGEXP '.c';
SELECT 'abc' REGEXP 'a.';
 
-- [...] 匹配括号内的任意单个字符
SELECT 'abc' REGEXP '[xyz]';
SELECT 'abc' REGEXP '[xaz]';

-- [^...] 注意^符合只有在[]内才是取反的意思,在别的地方都是表示开始处匹配
SELECT 'a' REGEXP '[^abc]';
SELECT 'x' REGEXP '[^abc]';
SELECT 'abc' REGEXP '[^a]';
 
-- a* 匹配0个或多个a,包括空字符串。 可以作为占位符使用.有没有指定字符都可以匹配到数据
 
SELECT 'stab' REGEXP '.ta*b';
SELECT 'stb' REGEXP '.ta*b';
SELECT '' REGEXP 'a*';
 
-- a+ 匹配1个或者多个a,但是不包括空字符
SELECT 'stab' REGEXP '.ta+b';
SELECT 'stb' REGEXP '.ta+b';



操作下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- a?  匹配0个或者1个a
SELECT 'stb' REGEXP '.ta?b';
SELECT 'stab' REGEXP '.ta?b';
SELECT 'staab' REGEXP '.ta?b';
 
-- a1|a2 匹配a1或者a2,
SELECT 'a' REGEXP 'a|b';
SELECT 'b' REGEXP 'a|b';
SELECT 'b' REGEXP '^(a|b)';
SELECT 'a' REGEXP '^(a|b)';
SELECT 'c' REGEXP '^(a|b)';
 
-- a{m} 匹配m个a
SELECT 'auuuuc' REGEXP 'au{4}c';
SELECT 'auuuuc' REGEXP 'au{3}c';

 -- a{m,n} 匹配m到n个a,包含m和n
SELECT 'auuuuc' REGEXP 'au{3,5}c';
SELECT 'auuuuc' REGEXP 'au{4,5}c';
SELECT 'auuuuc' REGEXP 'au{5,10}c';
 
-- (abc) abc作为一个序列匹配,不用括号括起来都是用单个字符去匹配,如果要把多个字符作为一个整体去匹配就需要用到括号,所以括号适合上面的所有情况。
SELECT 'xababy' REGEXP 'x(abab)y';
SELECT 'xababy' REGEXP 'x(ab)*y';
SELECT 'xababy' REGEXP 'x(ab){1,2}y';



多表操作

介绍

​ 实际开发中,一个项目通常需要很多张表才能完成。例如:一个商城项目就需要分类表(category)、商品表(products)、订单表(orders)等多张表。且这些表的数据之间存在一定的关系,接下来我们将在单表的基础上,一起学习多表方面的知识。

多表关系

MySQL多表之间的关系可以概括为:一对一、一对多/多对一关系,多对多

一对一关系

一个学生只有一张身份证;一张身份证只能对应一学生。

在任一表中添加唯一外键,指向另一方主键,确保一对一关系。

一般一对一关系很少见,遇到一对一关系的表最好是合并表。

一对一关系

一对多/多对一关系

部门和员工:

分析:一个部门有多个员工,一个员工只能对应一个部门;

实现原则:在多的一方建立外键,指向一的一方的主键。

一对多/多对一关系

多对多关系

学生和课程:

分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择;

原则:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,将多对多的关系,拆成一对多的关系,中间表至少要有两个外键,这两个外键分别指向原来的那两张表的主键。

​ 注:多对多的关系需要一个中间表作为桥梁

多对多关系

外键约束—概念

介绍

MySQL 外键约束(FOREIGN KEY)是表的一个特殊字段,经常与主键约束一起使用。对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表(父表),外键所在的表就是从表(子表)。

外键用来建立主表与从表的关联关系,为两个表的数据建立连接,约束两个表中数据的一致性和完整性。比如,一个水果摊,只有苹果、桃子、李子、西瓜等 4 种水果,那么,你来到水果摊要买水果就只能选择苹果、桃子、李子和西瓜,其它的水果都是不能购买的。

外键约束

特点

定义一个外键时,需要遵守下列规则:

  • 主表必须已经存在于数据库中,或者是当前正在创建的表。

  • 必须为主表定义主键。

  • ​ 主键不能包含空值,但允许在外键中出现空值。也就是说,只要外键的每个非空值出现在指定的主键中,这 个外键的内容就是正确的。

  • 在主表的表名后面指定列名或列名的组合。这个列或列的组合必须是主表的主键或候选键。

  • 外键中列的数目必须和主表的主键中列的数目相同。

  • 外键中列的数据类型必须和主表主键中对应列的数据类型相同。

外键约束—一对多关系

操作-创建外键约束

方式1-在创建表时设置外键约束

在 create table 语句中,通过 foreign key 关键字来指定外键,具体的语法格式如下:

1
[constraint <外键名>] foreign key 字段名 [,字段名2,…] references <主表名> 主键列1 [,主键列2,…];

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
create database mydb3; 
use mydb3;
-- 创建部门表
create table if not exists dept(
deptno varchar(20) primary key, -- 部门号
name varchar(20) -- 部门名字
);

-- 创建员工表
create table if not exists emp(
eid varchar(20) primary key , -- 员工编号
ename varchar(20), -- 员工名字
age int, -- 员工年龄
dept_id varchar(20), -- 员工所属部门
constraint emp_fk foreign key (dept_id) references dept (deptno) -- 外键约束
);

创建外键

方式2-在创建表时设置外键约束

外键约束也可以在修改表时添加,但是添加外键约束的前提是:从表中外键列中的数据必须与主表中主键列中的数据一致或者是没有数据。

1
alter table <数据表名> add constraint <外键名> foreign key(<列名>) references <主表名> (<列名>);

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 创建部门表
create table if not exists dept2(
deptno varchar(20) primary key , -- 部门号
name varchar(20) -- 部门名字
);
-- 创建员工表
create table if not exists emp2(
eid varchar(20) primary key , -- 员工编号
ename varchar(20), -- 员工名字
age int, -- 员工年龄
dept_id varchar(20) -- 员工所属部门
);
-- 创建外键约束
alter table emp2 add constraint dept_id_fk foreign key(dept_id) references dept2 (deptno);

创建外键

操作-在外键约束下的数据操作

验证外键约束的作用

1、数据插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 1、添加主表数据
-- 注意必须先给主表添加数据
insert into dept values('1001','研发部');
insert into dept values('1002','销售部');
insert into dept values('1003','财务部');
insert into dept values('1004','人事部');

-- 2、添加从表数据
-- 注意给从表添加数据时,外键列的值不能随便写,必须依赖主表的主键列
insert into emp values('1','乔峰',20, '1001');
insert into emp values('2','段誉',21, '1001');
insert into emp values('3','虚竹',23, '1001');
insert into emp values('4','阿紫',18, '1002');
insert into emp values('5','扫地僧',35, '1002');
insert into emp values('6','李秋水',33, '1003');
insert into emp values('7','鸠摩智',50, '1003');
insert into emp values('8','天山童姥',60, '1005'); -- 不可以

2、删除数据

1
2
3
4
5
6
7
8
9
10
-- 3、删除数据
/*
注意:
1:主表的数据被从表依赖时,不能删除,否则可以删除
2: 从表的数据可以随便删除
*/
delete from dept where deptno = '1001'; -- 不可以删除
delete from dept where deptno = '1004'; -- 可以删除
delete from emp where eid = '7'; -- 可以删除

操作-删除外键约束

当一个表中不需要外键约束时,就需要从表中将其删除。外键一旦删除,就会解除主表和从表间的关联关系

格式:

1
alter table <表名> drop foreign key <外键约束名>;

实现:

1
alter table emp2 drop foreign key dept_id_fk;

外键约束-多对多关系

介绍

在多对多关系中,A表的一行对应B的多行,B表的一行对应A表的多行,我们要新增加一个中间表,来建立多对多关系。

外键约束-多对多关系

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
-- 选择mydb3数据库
use mydb3;

-- 学生表和课程表(多对多)
-- 1 创建学生表student(左侧主表)
create table if not exists student(
sid int primary key auto_increment,
name varchar(20),
age int,
gender varchar(20)
);
-- 2 创建课程表course(右侧主表)
create table course(
cid int primary key auto_increment,
cidname varchar(20)
);

-- 3创建中间表student_course/score(从表)
create table score(
sid int,
cid int,
score double
);

-- 4建立外键约束(2次)
 
alter table score add foreign key(sid) references student(sid);
alter table score add foreign key(cid) references course(cid);
 
-- 5给学生表添加数据
insert into student values(1,'小龙女',18,'女'),(2,'阿紫',19,'女'),(3,'周芷若',20,'男');
-- 6给课程表添加数据
insert into course values(1,'语文'),(2,'数学'),(3,'英语');
-- 7给中间表添加数据
insert into score values(1,1,78),(1,2,75),(2,1,88),(2,3,90),(3,2,80),(3,3,65);

注意:修改和删除时,中间从表可以随便删除和修改,但是两边的主表受从表依赖的数据不能删除或者修改。

多表联合查询

介绍

多表查询就是同时查询两个或两个以上的表,因为有的时候用户在查看数据的时候,需要显示的数据来自多张表。

多表查询有以下分类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
交叉连接查询 [产生笛卡尔积,了解]
语法:select * from A,B;

内连接查询(使用的关键字 inner join -- inner可以省略)
隐式内连接(SQL92标准):select * from A,B where 条件;
显示内连接(SQL99标准):select * from A inner join B on 条件;

外连接查询(使用的关键字 outer join -- outer可以省略)
左外连接:left outer join
select * from A left outer join B on 条件;
右外连接:right outer join
select * from A right outer join B on 条件;
满外连接: full outer join
select * from A full outer join B on 条件;
子查询
select的嵌套
表自关联:
将一张表当成多张表来用

多表联合查询

准备查询数据

接下来准备多表查询需要数据,注意,外键约束对多表查询并无影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
use mydb3;

-- 创建部门表
create table if not exists dept3(
deptno varchar(20) primary key , -- 部门号
name varchar(20) -- 部门名字
);
 
-- 创建员工表
create table if not exists emp3(
eid varchar(20) primary key , -- 员工编号
ename varchar(20), -- 员工名字
age int, -- 员工年龄
dept_id varchar(20) -- 员工所属部门
);

-- 给dept3表添加数据
insert into dept3 values('1001','研发部');
insert into dept3 values('1002','销售部');
insert into dept3 values('1003','财务部');
insert into dept3 values('1004','人事部');

-- 给emp表添加数据
insert into emp3 values('1','乔峰',20, '1001');
insert into emp3 values('2','段誉',21, '1001');
insert into emp3 values('3','虚竹',23, '1001');
insert into emp3 values('4','阿紫',18, '1001');
insert into emp3 values('5','扫地僧',85, '1002');
insert into emp3 values('6','李秋水',33, '1002');
insert into emp3 values('7','鸠摩智',50, '1002');
insert into emp3 values('8','天山童姥',60, '1003');
insert into emp3 values('9','慕容博',58, '1003');
insert into emp3 values('10','丁春秋',71, '1005');

多表联合查询—交叉连接查询

  • 交叉连接查询返回被连接的两个表所有数据行的笛卡尔积
  • 笛卡尔积可以理解为一张表的每一行去和另外一张表的任意一行进行匹配
  • 假如A表有m行数据,B表有n行数据,则返回m*n行数据
  • 笛卡尔积会产生很多冗余的数据,后期的其他查询可以在该集合的基础上进行条件筛选

格式:

1
select * from1,表2,表3….;

实现:

1
2
3
-- 交叉连接查询
select * from dept3,emp3;

结果:

交叉连接查询

多表联合查询—内连接查询

内连接查询求多张表的交集

格式

1
2
隐式内连接(SQL92标准):select * from A,B where 条件; 
显示内连接(SQL99标准):select * from A inner join B on 条件;

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 查询每个部门的所属员工
select * from dept3,emp3 where dept3.deptno = emp3.dept_id;
select * from dept3 inner join emp3 on dept3.deptno = emp3.dept_id;

-- 查询研发部和销售部的所属员工
select * from dept3,emp3 where dept3.deptno = emp3.dept_id and name in( '研发部','销售部');
select * from dept3 join emp3 on dept3.deptno = emp3.dept_id and name in( '研发部','销售部');
 
-- 查询每个部门的员工数,并升序排序
select deptno,count(1) as total_cnt from dept3,emp3 where dept3.deptno = emp3.dept_id group by deptno order by total_cnt;
 
select deptno,count(1) as total_cnt from dept3 join emp3 on dept3.deptno = emp3.dept_id group by deptno order by total_cnt;

-- 查询人数大于等于3的部门,并按照人数降序排序
select deptno,count(1) as total_cnt from dept3,emp3 where dept3.deptno = emp3.dept_id group by deptno having total_cnt >= 3 order by total_cnt desc;
 
select deptno,count(1) as total_cnt from dept3 join emp3 on dept3.deptno = emp3.dept_id group by deptno having total_cnt >= 3 order by total_cnt desc;

外连接查询

  1. 外连接分为左外连接(left outer join)、右外连接(right outer join),满外连接(full outer join)。
  2. 注意:oracle里面有full join,可是在mysql对full join支持的不好。我们可以使用union来达到目的。

格式

1
2
3
4
5
6
7
左外连接:left outer join
select * from A left outer join B on 条件;
右外连接:right outer join
select * from A right outer join B on 条件;
满外连接: full outer join
select * from A full outer join B on 条件;

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 外连接查询
-- 查询哪些部门有员工,哪些部门没有员工
use mydb3;
select * from dept3 left outer join emp3 on dept3.deptno = emp3.dept_id;
 
-- 查询哪些员工有对应的部门,哪些没有
select * from dept3 right outer join emp3 on dept3.deptno = emp3.dept_id;
 
 
-- 使用union关键字实现左外连接和右外连接的并集(union后使用all代表不去重,不用代表去重)
select * from dept3 left outer join emp3 on dept3.deptno = emp3.dept_id
union
select * from dept3 right outer join emp3 on dept3.deptno = emp3.dept_id;

子查询

介绍

子查询就是指的在一个完整的查询语句之中,嵌套若干个不同功能的小查询,从而一起完成复杂查询的一种编写形式,通俗一点就是包含select嵌套的查询。

特点

子查询可以返回的数据类型一共分为四种:

  1. 单行单列:返回的是一个具体列的内容,可以理解为一个单值数据;
  2. 单行多列:返回一行数据中多个列的内容;
  3. 多行单列:返回多行记录之中同一列的内容,相当于给出了一个操作范围;
  4. 多行多列:查询返回的结果是一张临时表

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 查询年龄最大的员工信息,显示信息包含员工号、员工名字,员工年龄
-- 1:查询最大年龄:
select max(age) from emp3;
-- 2:让每一个员工的年龄和最大年龄进行比较,相等则满足条件(嵌套查询也称子查询)
select eid,ename,age from emp3 where age = (select max(age) from emp3); -- 单行单列,可以作为一个值来用
 
-- 查询年研发部和销售部的员工信息,包含员工号、员工名字
-- 方式1-关联查询
select * from dept3 a join emp3 b on a.deptno = b.dept_id and (name = '研发部' or name = '销售部');
-- 方式2-子查询
-- 2.1 先查询研发部和销售部的部门号:deptno 1001 和 1002
select deptno from dept3 where name = '研发部' or name = '销售部';
-- 2.2 查询哪个员工的部门号是1001 或者 1002
select * from emp3 where dept_id in (select deptno from dept3 where name = '研发部' or name = '销售部'); -- 多行单列,多个值
 
-- 查询研发部20岁以下的员工信息,包括员工号、员工名字,部门名字
-- 方式1-关联查询
select * from dept3 a join emp3 b on a.deptno = b.dept_id and (name = '研发部' and age < 20);
-- 方式2-子查询
-- 2.1 在部门表中查询研发部信息
select * from dept3 where name = '研发部'; -- 单行多列
-- 2.2 在员工表中查询年龄小于20岁的员工信息
select * from emp3 where age < 20;
-- 2.3 将以上两个查询的结果进行关联查询
select * from (select * from dept3 where name = '研发部') t1 join (select * from emp3 where age <20) t2 on t1.deptno = t2.dept_id; -- 多行多列


子查询关键字

介绍

在子查询中,有一些常用的逻辑关键字,这些关键字可以给我们提供更丰富的查询功能,主要关键字如下:

  1. ALL关键字
  2. ANY关键字
  3. SOME关键字
  4. IN关键字
  5. EXISTS关键字

子查询关键字—ALL

格式
1
2
3
selectfromwhere c > all(查询语句)
-- 等价于:
select ...from ... where c > result1 and c > result2 and c > result3
特点
  • ALL: 与子查询返回的所有值比较为true则返回true
  • ALL可以与=、>、>=、<、<=、<>结合是来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的其中的所有数据。
  • ALL表示指定列中的值必须要大于子查询集的每一个值,即必须要大于子查询集的最大值;如果是小于号即小于子查询集的最小值。同理可以推出其它的比较运算符的情况。
操作
1
2
3
4
5
-- 1.查询年龄大于‘1003’部门所有年龄的员工信息
select * from emp3 where age > all(select age from emp3 where dept_id = '1003');
-- 2.查询不属于任何一个部门的员工信息
select * from emp3 where dept_id != all(select deptno from dept3);

子查询关键字-ANY和SOME

格式
1
2
3
selectfromwhere c > any(查询语句)
--等价于:
select ...from ... where c > result1 or c > result2 or c > result3
特点
  • ANY:与子查询返回的任何值比较为true 则返回true
  • ANY可以与=、>、>=、<、<=、<>结合是来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的其中的任何一个数据。
  • 表示制定列中的值要大于子查询中的任意一个值,即必须要大于子查询集中的最小值。同理可以推出其它的比较运算符的情况。
  • SOME和ANY的作用一样,SOME可以理解为ANY的别名
操作
1
2
3
4
5
-- 查询年龄大于‘1003’部门任意一个员工年龄的员工信息
select * from emp3 where age > any(select age from emp3 where dept_id = '1003'); -- 去掉自己后面加:and dept_id != '1003' 即可。
-- 使用some(和any用处一样)
select * from emp3 where age > some(select age from emp3 where dept_id = '1003') and dept_id != '1003';

子查询关键字-IN

格式
1
2
3
selectfromwhere c in(查询语句)
--等价于:
select ...from ... where c = result1 or c = result2 or c = result3
特点
  • IN关键字,用于判断某个记录的值,是否在指定的集合中
  • 在IN关键字前边加上not可以将条件反过来
操作
1
2
-- 查询研发部和销售部的员工信息,包含员工号、员工名字
select eid,ename from emp3 where dept_id in (select deptno from dept3 where name = '研发部' or name = '销售部') ;

子查询关键字-EXISTS

格式
1
selectfromwhere exists(查询语句)
特点
  • 该子查询如果“有数据结果”(至少返回一行数据), 则该EXISTS() 的结果为“true”,外层查询执行
  • 该子查询如果“没有数据结果”(没有任何数据返回),则该EXISTS()的结果为“false”,外层查询不执行
  • EXISTS后面的子查询不返回任何实际数据,只返回真或假,当返回真时 where条件成立
  • 注意,EXISTS关键字,比IN关键字的运算效率高,因此,在实际开发中,特别是大数据量时,推荐使用EXISTS关键字
  • 有exists的时候是从外向内查询(在exists中判断),而其他的都是从内向外查询
操作
1
2
3
4
5
6
7
8
9
-- 查询公司是否有大于60岁的员工,有则输出
select * from emp3 a where exists(select * from emp3 b where a.age > 60);
-- 也可以使用 in 替换
select * from emp3 a where eid in(select eid from emp3 b where a.age > 60);

-- 查询有所属部门的员工信息
select * from emp3 a where exists(select * from dept3 b where a.dept_id = b.deptno);
-- 也可以使用 in 替换
select * from emp3 a where dept_id in(select deptno from dept3 b where a.dept_id = b.deptno);

自关联查询

概念

MySQL有时在信息查询时需要进行对表自身进行关联查询,即一张表自己和自己关联,一张表当成多张表来用。注意自关联时表必须给表起别名。

格式

1
2
3
select 字段列表 from1 a , 表1 b where 条件;
或者
select 字段列表 from1 a [left] join1 b on 条件;

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-- 创建表,并建立自关联约束
create table t_sanguo(
eid int primary key ,
ename varchar(20),
manager_id int,
foreign key (manager_id) references t_sanguo (eid) -- 添加自关联约束
);

-- 添加数据
insert into t_sanguo values(1,'刘协',NULL);
insert into t_sanguo values(2,'刘备',1);
insert into t_sanguo values(3,'关羽',2);
insert into t_sanguo values(4,'张飞',2);
insert into t_sanguo values(5,'曹操',1);
insert into t_sanguo values(6,'许褚',5);
insert into t_sanguo values(7,'典韦',5);
insert into t_sanguo values(8,'孙权',1);
insert into t_sanguo values(9,'周瑜',8);
insert into t_sanguo values(10,'鲁肃',8);
 
-- 进行关联查询
-- 1.查询每个三国人物及他的上级信息,如: 关羽 刘备
select * from t_sanguo a, t_sanguo b where a.manager_id = b.eid;
-- 也可以是(显示内连接查询):
select * from t_sanguo a join t_sanguo b on a.manager_id = b.eid;

-- 2.查询所有人物及上级(左外连接➕自关联)
select * from t_sanguo a left join t_sanguo b on a.manager_id = b.eid;
-- 仅显示名字
select a.ename,b.ename from t_sanguo a left join t_sanguo b on a.manager_id = b.eid;

-- 3.查询所以人物、上级,上上级 比如:张飞 刘备 刘协
select * from t_sanguo a left join t_sanguo b on a.manager_id = b.eid left join t_sanguo c on b.manager_id = c.eid;
-- 仅显示名字
select a.ename,b.ename,c.ename from t_sanguo a left join t_sanguo b on a.manager_id = b.eid left join t_sanguo c on b.manager_id = c.eid;

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
-- 创建test1数据库
create database test1;

-- 选择使用test1数据库
use test1;

-- 创建部门表
create table dept (
deptno int primary key, -- 部门编号
dname varchar(14), -- 部门名称
loc varchar(13) -- 部门地址
);

-- 插入数据(部门表)
insert into dept values (10,'accounting','new york');
insert into dept values (20,'research','dallas');
insert into dept values (30,'sales','chicago');
insert into dept values (40,'operations','boston');

-- 创建员工表
create table emp (
empno int primary key, -- 员工编号
ename varchar(10), -- 员工姓名
job varchar(9), -- 员工工作
mgr int, -- 员工直属领导编号
hiredate date, -- 入职时间
sal double, -- 工资
comm double, -- 奖金
deptno int -- 对应dept表的外键
);

-- 添加 部门 和 员工 之间的主外键关系
alter table emp add constraint foreign key emp(deptno) references dept(deptno);

-- 插入数据(员工表)
insert into emp values(7369,'smith','clerk',7902,'1980-12-17',800,null,20);
insert into emp values(7499,'allen','salesman',7698,'1981-02-20',1600,300,30);
insert into emp values(7521,'ward','salesman',7698,'1981-02-22',1250,500,30);
insert into emp values(7566,'jones','manager',7839,'1981-04-02',2975,null,20);
insert into emp values(7654,'martin','salesman',7698,'1981-09-28',1250,1400,30);
insert into emp values(7698,'blake','manager',7839,'1981-05-01',2850,null,30);
insert into emp values(7782,'clark','manager',7839,'1981-06-09',2450,null,10);
insert into emp values(7788,'scott','analyst',7566,'1987-07-03',3000,null,20);
insert into emp values(7839,'king','president',null,'1981-11-17',5000,null,10);
insert into emp values(7844,'turner','salesman',7698,'1981-09-08',1500,0,30);
insert into emp values(7876,'adams','clerk',7788,'1987-07-13',1100,null,20);
insert into emp values(7900,'james','clerk',7698,'1981-12-03',950,null,30);
insert into emp values(7902,'ford','analyst',7566,'1981-12-03',3000,null,20);
insert into emp values(7934,'miller','clerk',7782,'1981-01-23',1300,null,10);

-- 创建工资等级表
create table salgrade (
grade int, -- 等级
losal double, -- 最低工资
hisal double -- 最高工资
);

-- 插入数据(工资等级表)
insert into salgrade values (1,700,1200);
insert into salgrade values (2,1201,1400);
insert into salgrade values (3,1401,2000);
insert into salgrade values (4,2001,3000);
insert into salgrade values (5,3001,9999);

-- 练习:
-- 1、返回拥有员工的部门名、部门号。
select distinct d.dname,d.deptno from dept d join emp e on d.deptno = e.deptno;

-- 2、工资水平多于smith的员工信息。
select * from emp where sal > all(select sal from emp where ename = 'smith');

-- 3、返回员工和所属经理的姓名。
select a.ename,b.ename from emp a join emp b on a.mgr = b.empno;

-- 4、返回雇员的雇佣日期早于其经理雇佣日期的员工及其经理姓名。
select a.ename,a.hiredate,b.ename,b.hiredate from emp a join emp b on a.mgr = b.empno and a.hiredate < b.hiredate;

-- 5、返回员工姓名及其所在的部门名称。
select a.ename,b.dname from emp a join dept b on a.deptno = b.deptno;

-- 6、返回从事clerk工作的员工姓名和所在部门名称。
select a.ename,b.dname,a.job from emp a join dept b on a.deptno = b.deptno and job = 'clerk';

-- 7、返回部门号及其本部门的最低工资。
select deptno,min(sal) from emp group by deptno;

-- 8、返回销售部(sales)所有员工的姓名。
select a.ename from emp a join dept b on a.deptno = b.deptno and b.dname = 'sales';

-- 9、返回工资水平多于平均工资的员工。
select * from emp where sal > (select avg(sal) from emp);

-- 10、返回与scott从事相同工作的员工。
select * from emp where job = (select job from emp where ename = 'scott') and ename != 'scott'; -- and 以后是为了去掉自己

-- 11、返回与30部门员工工资水平相同的员工姓名与工资。
select * from emp where sal > all(select sal from emp where deptno = 30);

-- 12、返回员工工作及其从事此工作的最低工资。
select job,min(sal) from emp group by job;

-- 13、计算出员工的年薪,并且以年薪排序。
select *,(sal * 12 + ifnull(comm,0)) year_sal from emp order by year_sal desc;

-- 14、返回工资处于第四级别的员工的姓名。
select * from emp where sal between (select losal from salgrade where grade = 4) and (select hisal from salgrade where grade = 4);

-- 15、返回工资为二等级的职员名字、部门所在地。(select 后面可以按照题目写: c.grade,b.ename,a.loc)
-- 显示内连接
select * from dept a join emp b on a.deptno = b.deptno join salgrade c on c.grade = 2 and b.sal between c.losal and c.hisal;
-- 隐式内连接
select * from dept a,emp b,salgrade c where a.deptno = b.deptno and c.grade = 2 and b.sal between c.losal and c.hisal;


MySQL函数

基本介绍

概述

在MySQL中,为了提高代码重用性和隐藏实现细节,MySQL提供了很多函数。函数可以理解为别人封装好的模板代码。

分类

在MySQL中,函数非常多,主要可以分为以下几类:

  • 聚合函数
  • 数学函数
  • 字符串函数
  • 日期函数
  • 控制流函数
  • 窗口函数

聚合函数

概述

  • 在MySQL中,聚合函数主要由:count, sum, min, max, avg, 这些聚合函数我们之前都学过,不再重复。这里我们学习另外一个函数: group_concat(),该函数用户实现行的合并。
  • group_concat()函数首先根据group by指定的列进行分组,并且用分隔符分隔,将同一个分组中的值连接起来,返回一个字符串结果。

格式

1
group_concat([distinct] 字段名 [order by 排序字段 asc/desc] [separator '分隔符'])

说明:

(1)使用distinct可以排除重复值;

(2)如果需要对结果中的值进行排序,可以使用order by子句;

(3)separator是一个字符串值,默认为逗号。

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 创建并选择mydb4
create database mydb4;
use mydb4;
 
 -- 建表emp
create table emp(
emp_id int primary key auto_increment comment '编号',
emp_name char(20) not null default '' comment '姓名',
salary decimal(10,2) not null default 0 comment '工资',
department char(20) not null default '' comment '部门'
);
 
 -- 插入数据
insert into emp(emp_name,salary,department)
values('张晶晶',5000,'财务部'),('王飞飞',5800,'财务部'),('赵刚',6200,'财务部'),('刘小贝',5700,'人事部'),
('王大鹏',6700,'人事部'),('张小斐',5200,'人事部'),('刘云云',7500,'销售部'),('刘云鹏',7200,'销售部'),
('刘云鹏',7800,'销售部');

1
2
-- 将所有员工的名字合并成一行 
select group_concat(emp_name) from emp;

将所有员工的名字合并成一行

1
2
-- 指定分隔符合并 
select department,group_concat(emp_name separator ';' ) from emp group by department;

指定分隔符合并

1
2
-- 指定排序方式和分隔符 
select department,group_concat(emp_name order by salary desc separator ';' ) from emp group by department;

指定排序方式和分隔符

数学函数

函数名

由于表格行数有限,也为了方便查看,以下分为三张表来展示(加粗代表常用):

函数名 描述 实例
ABS(x) 返回 x 的绝对值 返回 -1 的绝对值:SELECT ABS(-1) – 返回1
CEIL(x) 返回大于或等于 x 的最小整数(向上取整) SELECT CEIL(1.5) – 返回2
FLOOR(x) 返回小于或等于 x 的最大整数(向下取整) 小于或等于 1.5 的整数:SELECT FLOOR(1.5) – 返回1
GREATEST(expr1, expr2, expr3, …) 返回列表中的最大值 返回以下数字列表中的最大值:SELECT GREATEST(3, 12, 34, 8, 25); – 34返回以下字符串列表中的最大值:SELECT GREATEST(“Google”, “Runoob”, “Apple”); – Runoob
LEAST(expr1, expr2, expr3, …) 返回列表中的最小值 返回以下数字列表中的最小值:SELECT LEAST(3, 12, 34, 8, 25); – 3返回以下字符串列表中的最小值:SELECT LEAST(“Google”, “Runoob”, “Apple”); – Apple
函数名 描述 实例
MAX(expression) 返回字段 expression 中的最大值 返回数据表 Products 中字段 Price 的最大值:SELECT MAX(Price) AS LargestPrice FROM Products;
MIN(expression) 返回字段 expression 中的最小值 返回数据表 Products 中字段 Price 的最小值:SELECT MIN(Price) AS MinPrice FROM Products;
MOD(x,y) 返回 x 除以 y 以后的余数 5 除于 2 的余数:SELECT MOD(5,2) – 1
PI() 返回圆周率(3.141593) SELECT PI() --3.141593
POW(x,y) 返回 x 的 y 次方 2 的 3 次方:SELECT POW(2,3) – 8
函数名 描述 实例
RAND() 返回 0 到 1 的随机数 SELECT RAND() --0.93099315644334
ROUND(x) 返回离 x 最近的整数(遵循四舍五入) SELECT ROUND(1.23456) --1
ROUND(x,y) 返回指定位数的小数(遵循四舍五入) SELECT ROUND(1.23456,3) –1.235
TRUNCATE(x,y) 返回数值 x 保留到小数点后 y 位的值(与 ROUND 最大的区别是不会进行四舍五入) SELECT TRUNCATE(1.23456,3) – 1.234

操作

1
2
3
4
5
6
-- 案例:通过数据库的函数,生成一个六位数的随机验证码。
select lpad(round(rand()*1000000,0),6,'0');

-- 四舍五入平均价格,保留两位小数。
use mydb2;
select category_id,round(avg(price),2) from product group by category_id;

字符串函数

字符串等的位置:从1开始;索引:从0开始。两者都是描述位置的,但是计数起点不同。

函数名

函数 描述 实例
CHAR_LENGTH(s) 返回字符串 s 的字符数 返回字符串 RUNOOB 的字符数SELECT CHAR_LENGTH(“RUNOOB”) AS LengthOfString;
LENGTH(s) 返回字符串 s 的字节数 SELECT LENGTH(“明天”) AS LengthOfString; – 6(utf-8 一个汉字为3个字节)
CHARACTER_LENGTH(s) 返回字符串 s 的字符数 返回字符串 RUNOOB 的字符数SELECT CHARACTER_LENGTH(“RUNOOB”) AS LengthOfString;
CONCAT(s1,s2…sn) 字符串 s1,s2 等多个字符串合并为一个字符串 合并多个字符串SELECT CONCAT("SQL ", "Runoob ", "Gooogle ", “Facebook”) AS ConcatenatedString;
CONCAT_WS(x, s1,s2…sn) 同 CONCAT(s1,s2,…) 函数,但是每个字符串之间要加上 x,x 可以是分隔符 合并多个字符串,并添加分隔符:SELECT CONCAT_WS(“-”, “SQL”, “Tutorial”, “is”, “fun!”)AS ConcatenatedString;
FIELD(s,s1,s2…) 返回第一个字符串 s 在字符串列表(s1,s2…)中的位置 返回字符串 c 在列表值中的位置:SELECT FIELD(“c”, “a”, “b”, “c”, “d”, “e”,“c”); 后面重复的’c’就不会输出位置,只能输出第一个查到的‘c’
函数 描述 实例
LTRIM(s) 去掉字符串 s 开始处的空格 去掉字符串 RUNOOB开始处的空格:SELECT LTRIM(" RUNOOB") AS LeftTrimmedString;-- RUNOOB
RTRIM(s) 去掉字符串 s 结尾处的空格 去掉字符串 RUNOOB结尾处的空格:SELECT RTRIM(" RUNOOB ") AS LeftTrimmedString;-- RUNOOB
MID(s,n,len) 从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len) 从字符串 RUNOOB 中的第 2 个位置截取 3个 字符:SELECT MID(“RUNOOB”, 2, 3) AS ExtractString; – UNO
POSITION(s1 IN s) 从字符串 s 中获取 s1 的开始位置 返回字符串 abc 中 b 的位置:SELECT POSITION(‘b’ in ‘abc’) – 2
REPLACE(s,s1,s2) 将字符串 s2 替代字符串 s 中的字符串 s1 将字符串 abc 中的字符 a 替换为字符 x:SELECT REPLACE(‘abca’,‘a’,‘x’) --xbcx
REVERSE(s) 将字符串s的顺序反过来 将字符串 abc 的顺序反过来:SELECT REVERSE(‘abc’) – cba
函数 描述 实例
LEFT(s,n) 返回字符串 s 的前 n 个字符 返回字符串 runoob 的前两个字符:SELECT left(‘runoob’,2) – ru
RIGHT(s,n) 返回字符串 s 的后 n 个字符 返回字符串 runoob 的后两个字符:SELECT RIGHT(‘runoob’,2) – ob
STRCMP(s1,s2) 比较字符串 s1 和 s2,如果 s1 与 s2 相等返回 0 ,如果 s1>s2 返回 1,如果 s1<s2 返回 -1 比较字符串:SELECT STRCMP(“runoob”, “runoob”); – 0
SUBSTR(s, start, length) 从字符串 s 的 start 位置截取长度为 length 的子字符串 从字符串 RUNOOB 中的第 2 个位置截取 3个 字符:SELECT SUBSTR(“RUNOOB”, 2, 3) AS ExtractString; – UNO
SUBSTRING(s, start, length) 从字符串 s 的 start 位置截取长度为 length 的子字符串 从字符串 RUNOOB 中的第 2 个位置截取 3个 字符:SELECT SUBSTRING(“RUNOOB”, 2, 3) AS ExtractString; – UNO
函数 描述 实例
TRIM(s) 去掉字符串 s 开始和结尾处的空格 去掉字符串 RUNOOB 的首尾空格:SELECT TRIM(’ RUNOOB ') AS TrimmedString;
UCASE(s) 将字符串转换为大写 将字符串 runoob 转换为大写:SELECT UCASE(“runoob”); – RUNOOB
UPPER(s) 将字符串转换为大写 将字符串 runoob 转换为大写:SELECT UPPER(“runoob”); – RUNOOB
LCASE(s) 将字符串 s 的所有字母变成小写字母 字符串 RUNOOB 转换为小写:SELECT LCASE(‘RUNOOB’) – runoob
LOWER(s) 将字符串 s 的所有字母变成小写字母 字符串 RUNOOB 转换为小写:SELECT LOWER(‘RUNOOB’) – runoob
LPAD(str,n,pad) 左填充,用字符串pad对str的左边进行填充,达到n个字符串长度 左填充:select lpad(‘01’,5,‘-’);
RPAD(str,n,pad) 右填充,用字符串pad对str的右边进行填充,达到n个字符串长度 右填充:select rpad(‘01’,5,‘-’);

操作

1
2
-- 案例:由于业务需求变更,企业员工的工号,统一为5位数,目前不足5位数的全部在前面补0,比如:1号员工的工号应该为00001
update emp set workno = lpad('1',5,'0');

日期函数

函数名

函数名 描述 实例
UNIX_TIMESTAMP() 返回从1970-01-01 00:00:00到当前毫秒值(时间戳) select UNIX_TIMESTAMP() -> 1632729059
UNIX_TIMESTAMP(DATE_STRING) 将制定日期转为毫秒值时间戳 SELECT UNIX_TIMESTAMP(‘2011-12-07 13:01:03’);
FROM_UNIXTIME(BIGINT UNIXTIME[, STRING FORMAT]) 将毫秒值时间戳转为指定格式日期 SELECT FROM_UNIXTIME(1598079966,‘%Y-%m-%d %H:%i:%s’); (1598079966,‘%Y-%m-%d %H:%i:%s’); -> 2020-08-22 15-06-06
CURDATE() 返回当前日期 SELECT CURDATE();-> 2018-09-19
CURRENT_DATE() 返回当前日期 SELECT CURRENT_DATE();-> 2018-09-19
函数名 描述 实例
CURRENT_TIME 返回当前时间 SELECT CURRENT_TIME();-> 19:59:02
CURTIME() 返回当前时间 SELECT CURTIME();-> 19:59:02
CURRENT_TIMESTAMP() 返回当前日期和时间 SELECT CURRENT_TIMESTAMP()-> 2018-09-19 20:57:43
DATE() 从日期或日期时间表达式中提取日期值 SELECT DATE(“2017-06-15”); -> 2017-06-15
DATEDIFF(d1,d2) 计算日期 d1->d2 之间相隔的天数 SELECT DATEDIFF(‘2001-01-01’,‘2001-02-02’)-> -32
函数名 描述 实例
TIMEDIFF(time1, time2) 计算时间差值 SELECT TIMEDIFF(“13:10:11”, “13:10:10”);-> 00:00:01
DATE_FORMAT(d,f) 按表达式 f的要求显示日期 d SELECT DATE_FORMAT(‘2011-11-11 11:11:11’,‘%Y-%m-%d %r’)-> 2011-11-11 11:11:11 AM
STR_TO_DATE(string, format_mask) 将字符串转变为日期 SELECT STR_TO_DATE(“August 10 2017”, “%M %d %Y”);-> 2017-08-10
DATE_SUB(date,INTERVAL expr type) 函数从日期减去指定的时间间隔。 Orders 表中 OrderDate 字段减去 2 天:SELECT OrderId,DATE_SUB(OrderDate,INTERVAL 2 DAY) AS OrderPayDate FROM Orders;
函数名 描述 实例
ADDDATE/DATE_ADD(d,INTERVAL expr type) 计算起始日期 d 加上一个时间段后的日期,type 值可以是:
MICROSECOND
SECOND
MINUTE
HOUR
DAY
WEEK
MONTH
QUARTER
YEAR
DAY_MINUTE
DAY_HOUR
YEAR_MONTH
SELECT DATE_ADD(“2017-06-15”, INTERVAL 10 DAY); -> 2017-06-25

SELECT DATE_ADD(“2017-06-15 09:34:21”, INTERVAL 15 MINUTE); -> 2017-06-15 09:49:21

SELECT DATE_ADD(“2017-06-15 09:34:21”, INTERVAL -3 HOUR); ->2017-06-15 06:34:21

SELECT DATE_ADD(“2017-06-15 09:34:21”, INTERVAL -3 HOUR); ->2017-04-15
函数名 描述 实例
DATE_ADD(d,INTERVAL expr type) 计算起始日期 d 加上一个时间段后的日期,type 值可以是:
SECOND_MICROSECOND
MINUTE_MICROSECOND
MINUTE_SECOND
HOUR_MICROSECOND
HOUR_SECOND
HOUR_MINUTE
DAY_MICROSECOND
DAY_SECOND
DAY_MINUTE
DAY_HOUR
YEAR_MONTH
SELECT DATE_ADD(“2017-06-15”, INTERVAL 10 DAY); -> 2017-06-25

SELECT DATE_ADD(“2017-06-15 09:34:21”, INTERVAL 15 MINUTE); -> 2017-06-15 09:49:21

SELECT DATE_ADD(“2017-06-15 09:34:21”, INTERVAL -3 HOUR); ->2017-06-15 06:34:21

SELECT DATE_ADD(“2017-06-15 09:34:21”, INTERVAL -3 HOUR); ->2017-04-15
EXTRACT(type FROM d) 从日期 d 中获取指定的值,type 指定返回的值。type可取值为:MICROSECOND
SECOND
MINUTE
HOUR ……
SELECT EXTRACT(MINUTE FROM ‘2011-11-11 11:11:11’) -> 11
LAST_DAY(d) 返回给给定日期的那一月份的最后一天 SELECT LAST_DAY(“2017-06-20”);-> 2017-06-30
MAKEDATE(year, day-of-year) 基于给定参数年份 year 和所在年中的天数序号 day-of-year 返回一个日期 SELECT MAKEDATE(2017, 3);-> 2017-01-03
函数名 描述 实例
YEAR(d) 返回年份 SELECT YEAR(“2017-06-15”);-> 2017
MONTH(d) 返回日期d中的月份值,1 到 12 SELECT MONTH(‘2011-11-11 11:11:11’)->11
DAY(d) 返回日期值 d 的日期部分 SELECT DAY(“2017-06-15”); -> 15
HOUR(t) 返回 t 中的小时值 SELECT HOUR(‘1:2:3’)-> 1
MINUTE(t) 返回 t 中的分钟值 SELECT MINUTE(‘1:2:3’)-> 2
SECOND(t) 返回 t 中的秒钟值 SELECT SECOND(‘1:2:3’)-> 3
QUARTER(d) 返回日期d是第几季节,返回 1 到 4 SELECT QUARTER(‘2011-11-11 11:11:11’)-> 4
函数名 描述 实例
MONTHNAME(d) 返回日期当中的月份名称,如 November SELECT MONTHNAME(‘2011-11-11 11:11:11’)-> November
DAYNAME(d) 返回日期 d 是星期几,如 Monday,Tuesday SELECT DAYNAME(‘2011-11-11 11:11:11’)->Friday
DAYOFMONTH(d) 计算日期 d 是本月的第几天 SELECT DAYOFMONTH(‘2011-11-11 11:11:11’)->11
DAYOFWEEK(d) 日期 d 今天是星期几,1 星期日,2 星期一,以此类推 SELECT DAYOFWEEK(‘2011-11-11 11:11:11’)->6
DAYOFYEAR(d) 计算日期 d 是本年的第几天 SELECT DAYOFYEAR(‘2011-11-11 11:11:11’)->315
函数名 描述 实例
WEEK(d) 计算日期 d 是本年的第几个星期,范围是 0 到 53 SELECT WEEK(‘2011-11-11 11:11:11’)-> 45
WEEKDAY(d) 日期 d 是星期几,0 表示星期一,1 表示星期二 SELECT WEEKDAY(“2017-06-15”);-> 3
WEEKOFYEAR(d) 计算日期 d 是本年的第几个星期,范围是 0 到 53 SELECT WEEKOFYEAR(‘2011-11-11 11:11:11’)-> 45
YEARWEEK(date, mode) 返回年份及第几周(0到53),mode 中 0 表示周天,1表示周一,以此类推 SELECT YEARWEEK(“2017-06-15”);-> 201724
NOW() 返回当前日期和时间 SELECT NOW()-> 2018-09-19 20:57:43

操作

1
2
-- 案例:查询所有员工的入职天数,并根据入职天数倒序排序。
select ename, datediff(curdate(), hiredate) hiredays from emp order by hiredays desc;

控制流函数

if逻辑判断语句

格式 解释 案例
IF(expr,v1,v2) 如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。 SELECT IF(1 > 0,‘正确’,‘错误’) ->正确
IFNULL(v1,v2) 如果 v1 的值不为 NULL,则返回 v1,否则返回 v2。 SELECT IFNULL(null,‘Hello Word’) ->Hello Word
ISNULL(expression) 判断表达式是否为 NULL SELECT ISNULL(NULL); ->1
NULLIF(expr1, expr2) 比较两个字符串,如果字符串 expr1 与 expr2 相等 返回 NULL,否则返回 expr1 SELECT NULLIF(25, 25); ->null

case when语句

格式 解释 操作
CASE expression
WHEN condition1 THEN result1 WHEN condition2 THEN result2 … WHEN conditionN THEN resultN
ELSE result
END
CASE 表示函数开始,END 表示函数结束。如果 condition1 成立,则返回 result1, 如果 condition2 成立,则返回 result2,当全部不成立则返回 result,而当有一个成立之后,后面的就不执行了。 select case 100 when 50 then ‘tom’ when 100 then ‘mary’else ‘tim’ end ;
select case when 1=2 then ‘tom’ when 2=2 then ‘mary’ else’tim’ end ;
案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
-- 案例1:

use mydb4;
-- 创建订单表
create table orders(
oid int primary key, -- 订单id
price double, -- 订单价格
payType int -- 支付类型(1:微信支付 2:支付宝支付 3:银行卡支付 4:其他)
);

-- 插入数据
insert into orders values(1,1200,1);
insert into orders values(2,1000,2);
insert into orders values(3,200,3);
insert into orders values(4,3000,1);
insert into orders values(5,1500,2);

-- 操作
-- 方式1
select
*,
case payType
when 1 then '微信支付'
when 2 then '支付宝支付'
when 3 then '银行卡支付'
else
'其他支付方式'
end as payTypeStr
from orders;

-- 方式2
select
*,
case
when payType=1 then '微信支付'
when payType=2 then '支付宝支付'
when payType=3 then '银行卡支付'
else
'其他支付方式'
end as payTypeStr
from orders;



-- 案例2:统计班级各个学员的成绩,展示的规则如下:
-- >= 85,展示优秀
-- >= 60,展示及格
-- 否则,展示不及格

use mydb4;

-- 创建表并插入数据
create table score(
id int comment 'ID',
name varchar(20) comment '姓名',
math int comment '数学',
english int comment '英语',
chinese int comment '语文'
) comment '学员成绩表';

insert into score(id,name,math,english,chinese) values(1,'Tom',67,88,95),(2,'Rose',23,66,90),(3,'Jack',56,98,76);


-- 操作
select
id,
name,
case when math >= 85 then '优秀' when math >=60 then '及格' else '不及格' end '数学',
case when english >= 85 then '优秀' when english >=60 then '及格' else '不及格' end '英语',
case when chinese >= 85 then '优秀' when chinese >=60 then '及格' else '不及格' end '语文'
from score;

窗口函数

介绍

  • MySQL 8.0 新增窗口函数 ,窗口函数又被称为开窗函数,与Oracle 窗口函数类似,属于MySQL的一大特点。
  • 非聚合窗口函数是相对于聚函数来说的。聚合函数是对一组数据计算后返回单个值(即分组),非聚合函数一次只会处理一行数据。窗口聚合函数在行记录上计算某个字段的结果时,可将窗口范围内的数据输入到聚合函数中,并不改变行数。

聚合函数-窗口函数

分类

窗口函数

另外还有开窗聚合函数: SUM,AVG,MIN,MAX

语法结构

1
2
3
4
5
window_function ( expr ) OVER ( 
PARTITION BY ...
ORDER BY ...
frame_clause
)

其中,window_function 是窗口函数的名称;expr 是参数,有些函数不需要参数;OVER子句包含三个选项:

  • 分区(PARTITION BY):

PARTITION BY选项用于将数据行拆分成多个分区(组),它的作用类似于GROUP BY分组。如果省略了 PARTITION BY,所有的数据作为一个组进行计算

  • 排序(ORDER BY):

OVER 子句中的ORDER BY选项用于指定分区内的排序方式,与 ORDER BY 子句的作用类似

  • 以及窗口大小(frame_clause):

frame_clause选项用于在当前分区内指定一个计算窗口,也就是一个与当前行相关的数据子集。

序号函数

序号函数有三个:ROW_NUMBER()、RANK()、DENSE_RANK(),可以用来实现分组排序,并添加序号。

格式
1
2
3
4
row_number()|rank()|dense_rank() over ( 
partition by ...
order by ...
)
操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use mydb4; 
create table employee(
dname varchar(20), -- 部门名
eid varchar(20),
ename varchar(20),
hiredate date, -- 入职日期
salary double -- 薪资
);

-- 插入数据
insert into employee values('研发部','1001','刘备','2021-11-01',3000);
insert into employee values('研发部','1002','关羽','2021-11-02',5000);
insert into employee values('研发部','1003','张飞','2021-11-03',7000);
insert into employee values('研发部','1004','赵云','2021-11-04',7000);
insert into employee values('研发部','1005','马超','2021-11-05',4000);
insert into employee values('研发部','1006','黄忠','2021-11-06',4000);
 
insert into employee values('销售部','1007','曹操','2021-11-01',2000);
insert into employee values('销售部','1008','许褚','2021-11-02',3000);
insert into employee values('销售部','1009','典韦','2021-11-03',5000);
insert into employee values('销售部','1010','张辽','2021-11-04',6000);
insert into employee values('销售部','1011','徐晃','2021-11-05',9000);
insert into employee values('销售部','1012','曹洪','2021-11-06',6000);


row_number()

1
2
3
4
5
6
7
-- 对每个部门的员工按照薪资降序排序,并给出排名
select
dname,
ename,
salary,
row_number() over(partition by dname order by salary desc) as rn
from employee;

row_number()

rank()

1
2
3
4
5
6
7
-- 对每个部门的员工按照薪资排序,并给出排名 rank
select
dname,
ename,
salary,
rank() over(partition by dname order by salary desc) as rn
from employee;

rank()

dense_rank()

1
2
3
4
5
6
7
-- 对每个部门的员工按照薪资排序,并给出排名 dense-rank
select
dname,
ename,
salary,
dense_rank() over(partition by dname order by salary desc) as rn
from employee;

dense_rank()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--求出每个部门薪资排在前三名的员工- 分组求TOPN
select
*
from
(
select
dname,
ename,
salary,
dense_rank() over(partition by dname order by salary desc) as rn
from employee
)t
where t.rn <= 3

分组求TOPN

1
2
3
4
5
6
7
8
9
-- 对所有员工进行全局排序(不分组)
-- 不加partition by表示全局排序
select
dname,
ename,
salary,
dense_rank() over( order by salary desc) as rn
from employee;

全局排序(不分组)

开窗聚合函数- SUM,AVG,MIN,MAX

概念

在窗口中每条记录动态地应用聚合函数(SUM()、AVG()、MAX()、MIN()、COUNT()),可以动态计算在指定的窗口内的各种聚合函数值。

操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
-- 开窗聚合函数

select
dname,
ename,
salary,
sum(salary) over(partition by dname order by hiredate) as pv1
from employee;
 
select
dname,
ename,
salary,
sum(salary) over(partition by dname) as pv3
from employee; -- 如果没有order by排序语句 默认把分组内的所有数据进行sum操作

select
dname,
ename,
salary,
sum(salary) over(partition by dname order by hiredate rows between unbounded preceding and current row) as c1
from employee; -- rows代表行,between从哪里到哪里,unbounded preceding代表 从开头 开始 到当前行current row 结束
 
select
dname,
ename,
salary,
sum(salary) over(partition by dname order by hiredate rows between 3 preceding and current row) as c1
from employee; -- rows代表行,between从哪里到哪里,3 preceding代表 从当前行向上三行 开始 到当前行current row 结束

select
dname,
ename,
salary,
sum(salary) over(partition by dname order by hiredate rows between 3 preceding and 1 following) as c1
from employee; -- rows代表行,between从哪里到哪里,3 preceding代表 从当前行向上三行 开始 到当前行下一行 1 following 结束


select
dname,
ename,
salary,
sum(salary) over(partition by dname order by hiredate rows between current row and unbounded following) as c1
from employee; -- rows代表行,between从哪里到哪里,current row代表 从当前行 开始 到最后 unbounded following 结束

分布函数-(CUME_DIST)

介绍
  • 用途:分组内小于、等于当前rank值的行数 / 分组内总行数
  • 应用场景:查询小于等于当前薪资(salary)的比例
操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
select  
dname,
ename,
salary,
cume_dist() over(order by salary) as rn1, -- 没有partition语句 所有的数据位于一组
cume_dist() over(partition by dname order by salary) as rn2
from employee;

/*
rn1: 没有partition,所有数据均为1组,总行数为12,
第一行:小于等于3000的行数为3,因此,3/12=0.25
第二行:小于等于4000的行数为5,因此,5/12=0.4166666666666667
rn2: 按照部门分组,dname='研发部'的行数为6,
第一行:研发部小于等于3000的行数为1,因此,1/6=0.16666666666666666
*/

分布函数-(CUME_DIST)

分布函数-(PERCENT_RANK)

介绍
  • 用途:每行按照公式(rank-1) / (rows-1)进行计算。其中,rank为RANK()函数产生的序号,rows为当前窗口的记录总行数;
  • 应用场景:不常用。
操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
select 
dname,
ename,
salary,
rank() over(partition by dname order by salary desc ) as rn,
percent_rank() over(partition by dname order by salary desc ) as rn2
from employee;

/*
rn2:
第一行: (1 - 1) / (6 - 1) = 0
第二行: (1 - 1) / (6 - 1) = 0
第三行: (3 - 1) / (6 - 1) = 0.4
*/

分布函数-(PERCENT_RANK)

前后函数-LAG和LEAD

介绍
  • 用途:返回位于当前行的前n行(LAG(expr,n))或后n行(LEAD(expr,n))的expr的值
  • 应用场景:查询前1名同学的成绩和当前同学成绩的差值
操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- lag的用法
select
dname,
ename,
salary,
hiredate,
lag(hiredate,1,'2000-01-01') over(partition by dname order by hiredate) as last_1_time,
lag(hiredate,2) over(partition by dname order by hiredate) as last_2_time
from employee;

/*
last_1_time: 指定了往上第1行的值,default为'2000-01-01'
第一行,往上1行为null,因此取默认值 '2000-01-01'
第二行,往上1行值为第一行值,2021-11-01
第三行,往上1行值为第二行值,2021-11-02
last_2_time: 指定了往上第2行的值,为指定默认值
第一行,往上2行为null
第二行,往上2行为null
第四行,往上2行为第二行值,2021-11-01
第七行,往上2行为第五行值,2021-11-02
*/


-- lead的用法
select
dname,
ename,
salary,
hiredate,
lead(hiredate,1,'2000-01-01') over(partition by dname order by hiredate) as last_1_time,
lead(hiredate,2) over(partition by dname order by hiredate) as last_2_time
from employee;

LAG(expr,n)

LAG(expr,n)

LEAD(expr,n)

LEAD(expr,n)

头尾函数-FIRST_VALUE和LAST_VALUE

介绍

用途:返回第一个(FIRST_VALUE(expr))或最后一个(LAST_VALUE(expr))expr的值

应用场景:截止到当前,按照日期排序查询第1个入职和最后1个入职员工的薪资

操作
1
2
3
4
5
6
7
8
9
10
-- 注意,  如果不指定ORDER BY,则进行排序混乱,会出现错误的结果
select
dname,
ename,
hiredate,
salary,
first_value(salary) over(partition by dname order by hiredate) as first,
last_value(salary) over(partition by dname order by hiredate) as last
from employee;

头尾函数-FIRST_VALUE和LAST_VALUE

其他函数-NTH_VALUE(expr, n)、NTILE(n)

介绍-NTH_VALUE(expr,n)
  • 用途:返回窗口中第n个expr的值。expr可以是表达式,也可以是列名
  • 应用场景:截止到当前薪资,显示每个员工的薪资中排名第2或者第3的薪资
操作
1
2
3
4
5
6
7
8
9
10
-- 查询每个部门截止目前薪资排在第二和第三的员工信息
select
dname,
ename,
hiredate,
salary,
nth_value(salary,2) over(partition by dname order by hiredate) as second_score,
nth_value(salary,3) over(partition by dname order by hiredate) as third_score
from employee;

NTH_VALUE(expr,n)

介绍-NTILE(n)
  • 用途:将分区中的有序数据分为n个等级,记录等级数
  • 应用场景:将每个部门员工按照入职日期分成3组
操作
1
2
3
4
5
6
7
8
9
-- 根据入职日期将每个部门的员工分成3组
select
dname,
ename,
hiredate,
salary,
ntile(3) over(partition by dname order by hiredate ) as rn
from employee;

NTILE(n)

练习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 取出每个部门的第一组员工
select
*
from
(
SELECT
dname,
ename,
hiredate,
salary,
NTILE(3) OVER(PARTITION BY dname ORDER BY hiredate ) AS rn
FROM employee
)t
where t.rn = 1;

每个部门的第一组员工

MySQL的视图

介绍

  • 视图(view)是一个虚拟表,非真实存在,其本质是根据SQL语句获取动态的数据集,并为其命名,用户使用时只需使用视图名称即可获取结果集,并可以将其当作表来使用。(就是原表的一个映射)
  • 数据库中只存放了视图的定义,而并没有存放视图中的数据。这些数据存放在原来的表中。
  • 使用视图查询数据时,数据库系统会从原来的表中取出对应的数据。因此,视图中的数据是依赖于原来的表中的数据的。一旦表中的数据发生改变,显示在视图中的数据也会发生改变。

作用

  • 简化代码,可以把重复使用的查询封装成视图重复使用,同时可以使复杂的查询易于理解和使用。
  • 安全原因,如果一张表中有很多数据,很多信息不希望让所有人看到,此时可以使用视图视,如:社会保险基金表,可以用视图只显示姓名,地址,而不显示社会保险号和工资数等,可以对不同的用户,设定不同的视图。

视图的创建

格式

创建视图的语法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create [or replace] [algorithm = {undefined | merge | temptable}]
 
view view_name [(column_list)]
 
as select_statement
 
[with [cascaded | local] check option]

参数说明:
1)algorithm:可选项,表示视图选择的算法。
2)view_name :表示要创建的视图名称。
3)column_list:可选项,指定视图中各个属性的名词,默认情况下与SELECT语句中的查询的属性相同。
4)select_statement :表示一个完整的查询语句,将查询记录导入视图中。
5)[with [cascaded | local] check option] :可选项,表示更新视图时要保证在该视图的权限范围之内。

数据准备

创建 数据库mydb6_view,然后在该数据库下执行sql脚本view_data.sql 导入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
create database mydb6_view;

-- 创建部门表并插入数据
create table dept(
deptno int primary key,
dname varchar(20),
loc varchar(20)
);

insert into dept values(10, '教研部','北京'),
(20, '学工部','上海'),
(30, '销售部','广州'),
(40, '财务部','武汉');


-- 创建员工表并插入数据
create table emp(
empno int primary key,
ename varchar(20),
job varchar(20),
mgr int,
hiredate date,
sal numeric(8,2),
comm numeric(8, 2),
deptno int,
-- FOREIGN KEY (mgr) REFERENCES emp(empno),
FOREIGN KEY (deptno) REFERENCES dept(deptno) ON DELETE SET NULL ON UPDATE CASCADE
);

INSERT INTO emp VALUES
(1001, '甘宁', '文员', 1013, '2000-12-17', 8000.00, NULL, 20),
(1002, '黛绮丝', '销售员', 1006, '2001-02-20', 16000.00, 3000.00, 30),
(1003, '殷天正', '销售员', 1006, '2001-02-22', 12500.00, 5000.00, 30),
(1004, '刘备', '经理', 1009, '2001-4-02', 29750.00, NULL, 20),
(1005, '谢逊', '销售员', 1006, '2001-9-28', 12500.00, 14000.00, 30),
(1006, '关羽', '经理', 1009, '2001-05-01', 28500.00, NULL, 30),
(1007, '张飞', '经理', 1009, '2001-09-01', 24500.00, NULL, 10),
(1008, '诸葛亮', '分析师', 1004, '2007-04-19', 30000.00, NULL, 20),
(1009, '曾阿牛', '董事长', NULL, '2001-11-17', 50000.00, NULL, 10),
(1010, '韦一笑', '销售员', 1006, '2001-09-08', 15000.00, 0.00, 30),
(1011, '周泰', '文员', 1008, '2007-05-23', 11000.00, NULL, 20),
(1012, '程普', '文员', 1006, '2001-12-03', 9500.00, NULL, 30),
(1013, '庞统', '分析师', 1004, '2001-12-03', 30000.00, NULL, 20),
(1014, '黄盖', '文员', 1007, '2002-01-23', 13000.00, NULL, 10);



-- 创建工资等级表并插入数据
create table salgrade (
grade int, -- 等级
losal double, -- 最低工资
hisal double -- 最高工资
);

-- 插入数据
insert into salgrade values (1,7000,12000);
insert into salgrade values (2,12010,14000);
insert into salgrade values (3,14010,20000);
insert into salgrade values (4,20010,30000);
insert into salgrade values (5,30010,99990);

操作

1
2
3
4
5
6
7
-- 创建视图
create or replace view view1_emp
as
select ename,job from emp;

-- 查看表和视图
show full tables;

修改视图

修改视图是指修改数据库中已存在的表的定义。当基本表的某些字段发生改变时,可以通过修改视图来保持视图和基本表之间一致。MySQL中通过CREATE OR REPLACE VIEW语句来创建视图和ALTER VIEW语句来修改视图。

格式

1
alter view 视图名 as select语句

操作

1
2
3
alter view view1_emp
as
select a.deptno,a.dname,a.loc,b.ename,b.sal from dept a, emp b where a.deptno = b.deptno;

更新视图

介绍

某些视图是可更新的。也就是说,可以在UPDATE、DELETE或INSERT等语句中使用它们,以更新基表的内容。对于可更新的视图,在视图中的行和基表中的行之间必须具有一对一的关系。如果视图包含下述结构中的任何一种,那么它就是不可更新的:

  • 聚合函数(SUM(), MIN(), MAX(), COUNT()等)
  • DISTINCT
  • GROUP BY
  • ORDER BY
  • HAVING
  • UNION或UNION ALL
  • 位于选择列表中的子查询
  • JOIN
  • FROM子句中的不可更新视图
  • WHERE子句中的子查询,引用FROM子句中的表。
  • 仅引用文字值(在该情况下,没有要更新的基本表)

视图中虽然可以更新数据,但是有很多的限制。一般情况下,最好将视图作为查询数据的虚拟表,而不要通过视图更新数据。因为,使用视图更新数据时,如果没有全面考虑在视图中更新数据的限制,就可能会造成数据更新失败。

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
--  ---------更新视图-------
create or replace view view1_emp
as
select ename,job from emp;
 
update view1_emp set ename = '周瑜' where ename = '鲁肃'; -- 可以修改
insert into view1_emp values('孙权','文员'); -- 不可以插入

-- ----------视图包含聚合函数不可更新--------------
create or replace view view2_emp
as
select count(*) cnt from emp;
 
insert into view2_emp values(100);
update view2_emp set cnt = 100


-- ----------视图包含distinct不可更新---------
create or replace view view3_emp
as
select distinct job from emp;
 
insert into view3_emp values('财务');
 
-- ----------视图包含goup by 、having不可更新------------------
 
create or replace view view4_emp
as
select deptno ,count(*) cnt from emp group by deptno having cnt > 2;
 
insert into view4_emp values(30,100);


-- ----------------视图包含union或者union all不可更新----------------
create or replace view view5_emp
as
select empno,ename from emp where empno <= 1005
union
select empno,ename from emp where empno > 1005;
 
insert into view5_emp values(1015,'韦小宝');

-- -------------------视图包含子查询不可更新--------------------
create or replace view view6_emp
as
select empno,ename,sal from emp where sal = (select max(sal) from emp);
 
insert into view6_emp values(1015,'韦小宝',30000);


-- ----------------------视图包含join不可更新-----------------
create or replace view view7_emp
as
select dname,ename,sal from emp a join dept b on a.deptno = b.deptno;
 
insert into view7_emp(dname,ename,sal) values('行政部','韦小宝',30000);
 
-- --------------------视图包含常量文字值不可更新-------------------
create or replace view view8_emp
as
select '行政部' dname,'杨过' ename;
 
insert into view8_emp values('行政部','韦小宝');


其他操作

重命名视图

1
2
-- rename table 视图名 to 新视图名; 
rename table view1_emp to my_view1

删除视图

1
2
-- drop view 视图名[,视图名…];
drop view if exists view_student;

删除视图时,只能删除视图的定义,不会删除数据。

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-- 1:查询部门平均薪水最高的部门名称
-- 方式一:视图
create view avg_sal as select deptno,avg(sal) from emp group by deptno order by avg(sal) desc limit 1;

select dname from dept a ,avg_sal b where a.deptno = b.deptno;
 
-- 方式二:子查询
select dname from dept a ,(select deptno,avg(sal) from emp group by deptno order by avg(sal) desc limit 1) b
where a.deptno = b.deptno;
 
-- 2:查询员工比所属领导薪资高的部门名、员工名、员工领导编号
select * from dept x,
(select a.ename aname ,a.sal asal,b.ename bname,b.sal bsal,a.deptno
from emp a, emp b
where a.mgr = b.empno and a.sal > b.sal) y
where x.deptno = y.deptno;

-- 3:查询工资等级为4级,2000年以后入职的工作地点为北京的员工编号、姓名和工资,并查询出薪资在前三名的员工信息
create view xxx
as
SELECT e.empno,e.ename,e.sal,e.hiredate
FROM emp e,dept d,salgrade s
WHERE (e.sal BETWEEN losal AND hisal) AND s.GRADE = 4
AND year(e.hiredate) > '2000'
AND d.loc = '北京';
 
select * from
(
select
*,
dense_rank() over(order by sal desc ) rn
from xxx
) t
where t.rn <=3;

MySQL的存储过程

介绍

  1. 什么是存储过程
  • MySQL 5.0 版本开始支持存储过程。
  • 简单的说,存储过程就是一组SQL语句集,功能强大,可以实现一些比较复杂的逻辑功能,类似于JAVA语言中的方法;
  • 存储过就是数据库 SQL 语言层面的代码封装与重用。
  1. 有哪些特性
  • 有输入输出参数,可以声明变量,有if/else, case,while等控制语句,通过编写存储过程,可以实现复杂的逻辑功能;
  • 函数的普遍特性:模块化,封装,代码复用;
  • 速度快,只有首次执行需经过编译和优化步骤,后续被调用可以直接执行,省去以上步骤;

入门案例

格式

1
2
3
4
5
6
delimiter 自定义结束符号
create procedure 储存名([ in ,out ,inout ] 参数名 数据类形...)
begin
sql语句
end 自定义的结束符合
delimiter ;

操作-数据准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
-- 1:创建数据库和表 
create database mydb7_procedure;

-- 选择mydb7_procedure数据库
use mydb7_procedure;

-- 创建部门表并插入数据
create table dept(
deptno int primary key,
dname varchar(20),
loc varchar(20)
);

insert into dept values(10, '教研部','北京'),
(20, '学工部','上海'),
(30, '销售部','广州'),
(40, '财务部','武汉');


-- 创建员工表并插入数据
create table emp(
empno int primary key,
ename varchar(20),
job varchar(20),
mgr int,
hiredate date,
sal numeric(8,2),
comm numeric(8, 2),
deptno int,
-- FOREIGN KEY (mgr) REFERENCES emp(empno),
FOREIGN KEY (deptno) REFERENCES dept(deptno) ON DELETE SET NULL ON UPDATE CASCADE
);


INSERT INTO emp VALUES
(1001, '甘宁', '文员', 1013, '2000-12-17', 8000.00, NULL, 20),
(1002, '黛绮丝', '销售员', 1006, '2001-02-20', 16000.00, 3000.00, 30),
(1003, '殷天正', '销售员', 1006, '2001-02-22', 12500.00, 5000.00, 30),
(1004, '刘备', '经理', 1009, '2001-4-02', 29750.00, NULL, 20),
(1005, '谢逊', '销售员', 1006, '2001-9-28', 12500.00, 14000.00, 30),
(1006, '关羽', '经理', 1009, '2001-05-01', 28500.00, NULL, 30),
(1007, '张飞', '经理', 1009, '2001-09-01', 24500.00, NULL, 10),
(1008, '诸葛亮', '分析师', 1004, '2007-04-19', 30000.00, NULL, 20),
(1009, '曾阿牛', '董事长', NULL, '2001-11-17', 50000.00, NULL, 10),
(1010, '韦一笑', '销售员', 1006, '2001-09-08', 15000.00, 0.00, 30),
(1011, '周泰', '文员', 1008, '2007-05-23', 11000.00, NULL, 20),
(1012, '程普', '文员', 1006, '2001-12-03', 9500.00, NULL, 30),
(1013, '庞统', '分析师', 1004, '2001-12-03', 30000.00, NULL, 20),
(1014, '黄盖', '文员', 1007, '2002-01-23', 13000.00, NULL, 10);


-- 创建工资等级表并插入数据
create table salgrade (
grade int, -- 等级
losal double, -- 最低工资
hisal double -- 最高工资
);

-- 插入数据
insert into salgrade values (1,7000,12000);
insert into salgrade values (2,12010,14000);
insert into salgrade values (3,14010,20000);
insert into salgrade values (4,20010,30000);
insert into salgrade values (5,30010,99990);


-- 2:创建存储过程
delimiter $$
create procedure proc01()
begin
select empno,ename from emp;
end $$
delimiter ;

-- 调用存储过程
call proc01();

MySQL操作-变量定义

局部变量

格式

用户自定义,在begin/end块中有效

1
2
语法: 声明变量 declare var_name type [default var_value]; 
举例:declare nickname varchar(32);
操作
1
2
3
4
5
6
7
8
9
10
11
12
13
-- 定义局部变量
delimiter $$
create procedure proc02()
begin
declare var_name01 varchar(20) default 'aaa'; -- 定义局部变量
set var_name01 = 'zhangsan'; -- 给变量赋值
select var_name01; -- 输出变量的值
end $$
delimiter ;

-- 调用存储过程
call proc02();

MySQL 中还可以使用 SELECT…INTO 语句为变量赋值。其基本语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 格式
select col_name [...] into var_name[,...]
from table_name wehre condition

其中:
col_name 参数表示查询的字段名称;
var_name 参数是变量的名称;
table_name 参数指表的名称;
condition 参数指查询条件。

🌟注意:当将查询结果赋值给变量时,该查询语句的返回结果只能是单行单列。

-- 例子
delimiter $$
create procedure proc03()
begin
declare my_ename varchar(20) ;
select ename into my_ename from emp where empno=1001;
select my_ename;
end $$
delimiter ;

-- 调用存储过程
call proc03();

用户变量

格式

用户自定义,当前会话(连接)有效。类比java的成员变量

1
2
3
语法: 
@var_name
不需要提前声明,使用即声明
操作
1
2
3
4
5
6
7
8
9
delimiter $$
create procedure proc04()
begin
set @var_name01 = 'ZS';
end $$
delimiter;
call proc04() ;
select @var_name01 ; --可以看到结果

系统变量

介绍
  1. 系统变量又分为全局变量与会话变量。
  2. 全局变量在MYSQL启动的时候由服务器自动将它们初始化为默认值,这些默认值可以通过更改my.ini这个文件来更改。
  3. 会话变量在每次建立一个新的连接的时候,由MYSQL来初始化。MYSQL会将当前所有全局变量的值复制一份。来做为会话变量。
  4. 也就是说,如果在建立会话以后,没有手动更改过会话变量与全局变量的值,那所有这些变量的值都是一样的。
  5. 全局变量与会话变量的区别就在于,对全局变量的修改会影响到整个服务器,但是对会话变量的修改,只会影响到当前的会话(也就是当前的数据库连接)。
  6. 有些系统变量的值是可以利用语句来动态进行更改的,但是有些系统变量的值却是只读的,对于那些可以更改的系统变量,我们可以利用set语句进行更改。

系统变量-全局变量

由系统提供,在整个数据库有效。

格式
1
2
语法:
@@global.var_name
操作
1
2
3
4
5
6
7
8
-- 查看全局变量 
show global variables;
-- 查看某全局变量
select @@global.auto_increment_increment;
-- 修改全局变量的值
set global sort_buffer_size = 40000;
set @@global.sort_buffer_size = 40000;

系统变量-会话变量

由系统提供,当前会话(连接)有效

格式
1
2
语法:
@@session.var_name
操作
1
2
3
4
5
6
7
8
-- 查看会话变量
show session variables;
-- 查看某会话变量
select @@session.auto_increment_increment;
-- 修改会话变量的值
set session sort_buffer_size = 50000;
set @@session.sort_buffer_size = 50000 ;

存储过程传参-in

in 表示传入的参数, 可以传入数值或者变量,即使传入变量,并不会更改变量的值,可以内部更改,仅仅作用在函数范围内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 封装有参数的存储过程,传入员工编号,查找员工信息
delimiter $$
create procedure dec_param01(in param_empno varchar(20))
begin
select * from emp where empno = param_empno;
end $$
 
delimiter ;

call dec_param01('1001');

-- 案例
-- 封装有参数的存储过程,可以通过传入部门名和薪资,查询指定部门,并且薪资大于指定值的员工信息
delimiter $$
create procedure dec_param0x(in dname varchar(50),in sal decimal(7,2))
begin
select * from dept a, emp b where b.sal > sal and a.dname = dname;
end $$
 
delimiter ;

call dec_param0x('学工部',20000);
call dec_param0x('教研部',20000);

存储过程传参-out

out 表示从存储过程内部传值给调用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- ---------传出参数:out---------------------------------
use mydb7_procedure;
-- 封装有参数的存储过程,传入员工编号,返回员工名字
delimiter $$
create procedure proc08(in empno int ,out out_ename varchar(50) )
begin
select ename into out_ename from emp where emp.empno = empno;
end $$
 
delimiter ;
 
call proc08(1001, @o_ename);
select @o_ename;

-- 案例
-- 封装有参数的存储过程,传入员工编号,返回员工名字和薪资
delimiter $$
create procedure proc09(in empno int ,out out_ename varchar(50) ,out out_sal decimal(7,2))
begin
select ename,sal into out_ename,out_sal from emp where emp.empno = empno;
end $$
 
delimiter ;
 
call proc09(1001, @o_dname,@o_sal);
select @o_dname;
select @o_sal;

存储过程传参-inout

inout 表示从外部传入的参数经过修改后可以返回的变量,既可以使用传入变量的值也可以修改变量的值(即使函数执行完)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 传入员工名,拼接部门号,传入薪资,求出年薪
-- 关羽 ----> 30_关羽
delimiter $$
create procedure proc10(inout inout_ename varchar(50),inout inout_sal int)
begin
select concat(deptno,"_",inout_ename) into inout_ename from emp where ename = inout_ename;
set inout_sal = inout_sal * 12;
end $$
delimiter ;
set @inout_ename = '关羽';
set @inout_sal = 3000;
call proc10(@inout_ename, @inout_sal) ;
select @inout_ename ;
select @inout_sal ;

inout

存储过程传参-in,out, inout

总结

  • in 输入参数,意思说你的参数要传到存过过程的过程里面去,在存储过程中修改该参数的值不能被返回

  • out 输出参数:该值可在存储过程内部被改变,并向外输出

  • inout 输入输出参数,既能输入一个值又能传出来一个值)

流程控制-判断

格式

IF语句包含多个条件判断,根据结果为TRUE、FALSE执行语句,与编程语言中的if、else if、else语法类似,其语法格式如下:

1
2
3
4
5
-- 语法
if search_condition_1 then statement_list_1
[elseif search_condition_2 then statement_list_2] ...
[else statement_list_n]
end if

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
-- 输入学生的成绩,来判断成绩的级别:
/*
score < 60 :不及格
score >= 60 , score <80 :及格
score >= 80 , score < 90 :良好
score >= 90 , score <= 100 :优秀
score > 100 :成绩错误
*/
delimiter $$
create procedure proc_12_if(in score int)
begin
if score < 60
then
select '不及格';
elseif score < 80
then
select '及格' ;
elseif score >= 80 and score < 90
then
select '良好';
elseif score >= 90 and score <= 100
then
select '优秀';
else
select '成绩错误';
end if;
end $$
delimiter ;

call proc_12_if(120)

-- 输入员工的名字,判断工资的情况。
delimiter $$
create procedure proc12_if(in in_ename varchar(50))
begin
declare result varchar(20);
declare var_sal decimal(7,2);
select sal into var_sal from emp where ename = in_ename;
if var_sal < 10000
then set result = '试用薪资';
elseif var_sal < 20000
then set result = '转正薪资';
elseif var_sal >= 20000
then set result = '元老薪资';
else
set result = '查询名字不存在';
end if;
select result;
end$$
delimiter ;

call proc12_if('庞统');

流程控制-case

CASE是另一个条件判断的语句,类似于编程语言中的switch语法

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
-- 语法一(类比java的switch):
case case_value
when when_value then statement_list
[when when_value then statement_list] ...
[else statement_list]
end case
-- 语法二:
case
when search_condition then statement_list
[when search_condition then statement_list] ...
[else statement_list]
end case

-- 案例
-- 语法一
delimiter $$
create procedure proc14_case(in pay_type int)
begin
case pay_type
when 1
then
select '微信支付' ;
when 2 then select '支付宝支付' ;
when 3 then select '银行卡支付';
else select '其他方式支付';
end case ;
end $$
delimiter ;
 
call proc14_case(2);
call proc14_case(4);

-- 语法二
delimiter $$
create procedure proc_15_case(in score int)
begin
case
when score < 60
then
select '不及格';
when score < 80
then
select '及格' ;
when score >= 80 and score < 90
then
select '良好';
when score >= 90 and score <= 100
then
select '优秀';
else
select '成绩错误';
end case;
end $$
delimiter ;
 
call proc_15_case(88);

流程控制-循环

概述:

  • 循环是一段在程序中只出现一次,但可能会连续运行多次的代码。
  • 循环中的代码会运行特定的次数,或者是运行到特定条件成立时结束循环。

循环分类:

  • while
  • repeat
  • loop

循环控制:

  • leave 类似于 break,跳出,结束当前所在的循环;
  • iterate类似于 continue,继续,结束本次循环,继续下一次。

循环

流程控制-循环-while

格式

1
2
3
【标签:】while 循环条件 do
循环体;
end while【 标签】;

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
-- 选择 mydb7_procedure 数据库
use mydb7_procedure;

-- 创建测试表
create table user (
uid int primary key,
username varchar ( 50 ),
password varchar ( 50 )
);

-- 需求:向表中添加10条数据。
-- -------存储过程-while
delimiter $$
create procedure proc16_while1(in insertcount int)
begin
declare i int default 1;
label:while i<=insertcount do
insert into user(uid,username,password) values(i,concat('user-',i),'123456');
set i=i+1;
end while label;
end $$
delimiter ;
 
call proc16_while1(10);


-- -------存储过程-while + leave
truncate table user;
delimiter $$
create procedure proc16_while2(in insertcount int)
begin
declare i int default 1;
label:while i<=insertcount do
insert into user(uid,username,password) values(i,concat('user-',i),'123456');
if i=5 then leave label;
end if;
set i=i+1;
end while label;
end $$
delimiter ;
 
call proc16_while2(10);


-- -------存储过程-while+iterate
truncate table user;
delimiter $$
create procedure proc16_while3(in insertcount int)
begin
declare i int default 1;
label:while i<=insertcount do
set i=i+1;
if i=5 then iterate label;
end if;
insert into user(uid,username,password) values(i,concat('user-',i),'123456');
end while label;
end $$
delimiter ;
call proc16_while3(10);

流程控制-循环-repeat

格式

1
2
3
4
[标签:]repeat 
循环体;
until 条件表达式
end repeat [标签];

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- -------存储过程-循环控制-repeat 
use mydb7_procedure;
truncate table user;
 
 
delimiter $$
create procedure proc18_repeat(in insertCount int)
begin
declare i int default 1;
label:repeat
insert into user(uid, username, password) values(i,concat('user-',i),'123456');
set i = i + 1;
until i > insertCount
end repeat label;
select '循环结束';
end $$
delimiter ;
 
call proc18_repeat(100);

流程控制-循环-loop

格式

1
2
3
4
5
6
[标签:] loop
循环体;
if 条件表达式 then
leave [标签];
end if;
end loop;

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- -------存储过程-循环控制-loop
truncate table user;
 
delimiter $$
create procedure proc19_loop(in insertCount int)
begin
declare i int default 1;
label:loop
insert into user(uid, username, password) values(i,concat('user-',i),'123456');
set i = i + 1;
if i > 5
then
leave label;
end if;
end loop label;
select '循环结束';
end $$
delimiter ;
 
call proc19_loop(10);

MySQL的索引

介绍

  • 索引是通过某种算法,构建出一个数据模型,用于快速找出在某个列中有一特定值的行,不使用索引,MySQL必须从第一条记录开始读完整个表,直到找出相关的行,表越大,查询数据所花费的时间就越多,如果表中查询的列有一个索引,MySQL能够快速到达一个位置去搜索数据文件,而不必查看所有数据,那么将会节省很大一部分时间。

  • 索引类似一本书的目录,比如要查找’student’这个单词,可以先找到s开头的页然后向后查找,这个就类似索引。

索引的分类(按照实现的方式类分)

索引是存储引擎用来快速查找记录的一种数据结构,按照实现的方式类分,主要有Hash索引B+Tree索引

Hash索引

Hash索引有时会产生Hash冲突,优化使用公式能降低冲突。

Hash索引

B+Tree索引

B+Tree索引

索引的分类(按照功能划分)

按照功能划分,索引划为以下分类:

按照功能划分

索引的操作-创建索引-单列索引-普通索引

介绍
  • 单列索引:一个索引只包含单个列,但一个表中可以有多个单列索引;
    • 普通索引(NORMAL):MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。
格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
create database mydb5;
use mydb5;

-- 方式1-创建表的时候直接指定
create table student(
sid int primary key,
card_id varchar(20),
name varchar(20),
gender varchar(20),
age int,
birth date,
phone_num varchar(20),
score double,
index index_name(name) -- 给name列创建索引
);

-- 方式2-直接创建
-- create index indexname on tablename(columnname);
create index index_gender on student(gender);

-- 方式3-修改表结构(添加索引)
-- alter table tablename add index indexname(columnname)
alter table student add index index_age(age);

索引的操作-查看索引-单列索引-普通索引

操作
1
2
3
4
5
6
7
8
9
10
11
12
-- 1、查看数据库所有索引 
-- select * from mysql.`innodb_index_stats` a where a.`database_name` = '数据库名’;
select * from mysql.`innodb_index_stats` a where a.`database_name` = 'mydb5';

-- 2、查看表中所有索引
-- select * from mysql.`innodb_index_stats` a where a.`database_name` = '数据库名' and a.table_name like '%表名%’;
select * from mysql.`innodb_index_stats` a where a.`database_name` = 'mydb5' and a.table_name like '%student%';

-- 3、查看表中所有索引
-- show index from table_name;
show index from student;

索引的操作-删除索引

格式
1
2
3
drop index 索引名 on 表名; 
-- 或
alter table 表名 drop index 索引名;
操作
1
2
3
4
5
drop index index_gender on student;
-- 或
alter table student drop index index_name;


索引的操作-创建索引-单列索引-唯一索引

介绍

唯一索引与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:

操作-创建索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 方式1-创建表的时候直接指定
create table student2(
sid int primary key,
card_id varchar(20),
name varchar(20),
gender varchar(20),
age int,
birth date,
phone_num varchar(20),
score double,
unique index_card_id(card_id) -- 给card_id列创建索引
);
-- 方式2-直接创建
-- create unique index 索引名 on 表名(列名)
create unique index index_card_id on student2(card_id);

-- 方式3-修改表结构(添加索引)
-- alter table 表名 add unique [索引名] (列名)
alter table student2 add unique index_phone_num(phone_num)

操作-删除索引
1
2
3
drop index index_card_id on student2 
-- 或
alter table student2 drop index index_phone_num

文章作者: [Blue Eagle]

文章链接: [https://yjh021.github.io/2024/10/04/MySQL/]

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 [Blue Eagle]