MySQL基础
- 文章框架概览
- 一、数据库概述
- 二、DQL 数据查询语言
- 三、DML(Data Manipulate Language)数据操作语言
- 四、DDL(Data Define Languge)数据定义语言
- 五、TCL(Transaction Control Language)事务控制语言
- 六、视图
- 七、变量
- 八、存储过程和函数
- 九、流程控制结构
- 参考
文章框架概览
思维导图
数据库练习文件下载链接(不需要积分)
一、数据库概述
1. 相关概念
- DB: Database 存储数据的仓库,保存了一系列有组织的数据
- DBMS:数据库管理系统。数据库是通过DBMS创建和操作的容器。MySQL,SQL server,Oracle,DB2
- SQL:结构化查询语言,专门用来与数据库通信的语言
- SQL优点
- 几乎所有的DBMS都支持SQL
- 简单,使用灵活
- SQL优点
2. 使用数据库的好处
- 实现数据持久化存储
- 实现结构化查询,易于管理
3. 查看MySQL版本
两种方式,一种是通过bash查看,另一种是通过MySQL命令查看
-
提供bash查看
mysql -V mysql --version
-
通过MySQL命令查看
select version();
4. MySQL导入导出数据
4.1 导入数据
4.1.1 bash中通过命令导入
mysql -u用户名 -p密码 < 要导入的数据库文件(test.sql)
举例
mysql -uroot -p123456 < test.sql
4.1.2 source 命令导入
source 命令导入数据库需要先登录到数库终端
source 路径;
举例:导入桌面文件夹MySQL中的test.sql文件
source ~/Desktop/MySQL/test.sql;
4.2 导出数据
4.2.1 使用select … into outfile 语句导出数据
4.2.1.1 语法
简单导出
select * from 表名 into outfile 'filename';
# 把account 表中的数据导入到 /var/lib/mysql-files/
select * from account into outfile '/var/lib/mysql-files/account.txt';
导出csv格式的文件
生成一个文件,各值用逗号隔开
select * from account into outfile '/var/lib/mysql-files/account.txt'
fields terminated by ','
enclosed by '"'
lines terminated by '\r\n';
- SELECT…INTO OUTFILE 'file_name’形式的SELECT可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有FILE权限,才能使用此语法。
- 输出不能是一个已存在的文件。防止文件数据被篡改。
- 你需要有一个登陆服务器的账号来检索文件。否则 SELECT … INTO OUTFILE 不会起任何作用。
- 在UNIX中,该文件被创建后是可读的,权限由MySQL服务器所拥有。这意味着,虽然你就可以读取该文件,但可能无法将其删除。
4.2.1.2 导入文件提示 --secure-file-priv option 问题
错误提示:
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so i t cannot execute this statement
原因:因为在安装MySQL的时候限制了导入与导出的目录权限。只允许在规定的目录下才能导入。
查看secure-file-priv当前的值
show variables like "secure_file_priv";
本地value的值:
- (1)NULL,表示禁止。
- (2)如果value值有文件夹目录,则表示只允许该目录下文件(测试子目录也不行)。
- (3)如果为空,则表示不限制目录。
解决
- (1)把导入路径改为secure-file-priv对应的value中的文件夹目录
- (2)把secure-file-priv的value值修改为准备导入文件的放置路径
- (3)修改配置文件去掉导入的目录限制。可修改mysql配置文件(Windows下为my.ini, Linux下的my.cnf)
- 在[mysqld]下面,查看是否有:
secure_file_priv =
如上这样一行内容,如果没有,则手动添加。 - 如果存在如下行:
secure_file_priv = /var/lib/mysql-files
这样一行内容,表示限制为/var/lib/mysql-files文件夹。 - 如果存在如下行
secure_file_priv =
这样一行内容,表示不限制目录,等号一定要有,否则mysql无法启动。 - 修改完配置文件后,重启mysql生效。
- 关闭:service mysqld stop
- 启动:service mysqld start
- 在[mysqld]下面,查看是否有:
4.2.2 使用mysqldump导出SQL格式的数据
mysqldump 是 mysql 用于转存储数据库的实用程序。它主要产生一个 SQL 脚本,其中包含从头重新创建数据库所必需的命令 CREATE TABLE INSERT 等。
mysqldump -uroot -p 数据库名 表名 > 文件名
# 回车后会提示你输入密码
password:
举例:把practice.sql中的account表导入到桌面
mysqldump -uroot -p practice account > ~/Desktop/account.txt
password:xxxxxxxx
5. MySQL Ubuntu安装
- 1)sudo apt update
- 2)sudo apt-get install mysql-server mysql-client
- 3)安装过程中需要你输入MySQL 管理员用户(root)密码
- 4)(非必须)运行MySQL初始化安全脚本 sudo mysql_secure_installation
mysql_secure_installation脚本设置的东西:更改root密码、移除MySQL的匿名用户、禁止root远程登录、删除test数据库。使用上面的这些选项可以提高MySQL的安全
二、DQL 数据查询语言
1. 基础查询
语法
- 普通查询:
Select 查询列表 from 表名;
- 去重查询:
Select distinct 字段 from 表名;
不能是多个字段
注意:
- 查询列表:可以是表中的字段(多个字段用‘,’隔开),常量值,表达式,函数;
- 字符型和日期型必须用单引号引起,数值型不需要
- 查询的结果是一个虚拟的表格
2. 条件查询
select 查询列表 from 表名 where 条件;
- 执行顺序 3,1,2。先从表里查,然后过滤条件,最后执行select
- 逻辑运算符用于连接条件运算符
- 模糊查询:like 与通配符搭配使用;%任意多个字符,_任意单个字符
- \:转义字符;在语句后面加上
ESCAPE ‘字符’
可指定这个字符为转移字符
- Between … and … 包含临界值
- in列表的值类型必须一致,in不支持通配符
- 安全等于 <=> :可以用于判断null值与普通类型的值,=不能用于判断null值;
3. 排序查询
select 查询列表 from 表名 [where 条件] order by 排序列表 [asc | desc];
- sec升序,默认值;desc降序
- order by子句支持单个字段,多个字段,表达式,函数,别名
- 按多个字段排序,比如先按年薪降序,按姓名升序
一般放在查询语句后面,limit子句除外
4. 常见函数
select 函数名(实参列表) [from 表名];
4.1 单行函数
4.1.1 字符函数
函数 | 说明 |
---|---|
length(‘str’) | 返回字符长度 |
upper(参数) | 将字符变成大写 |
lower(参数) | 将字符变成小写 |
concat(str1,str2,…) | 拼接字符串 |
substr(str,pos) | 返回pos开始的字符串 |
substr(str,pos1,pos2) | 返回pos1开始到pos2的字符串 |
instr(str,substr) | 返回子字符串在字符串中的第一次出现的索引位置,找不到返回0 |
trim(‘字符’ from ‘str’) | 去除str中开始和结尾的字符,默认为空格 |
lpad(‘str’,n,‘字符’) | 用指定的字符实现左填充指定长度 |
rpad(‘str’,n,‘字符’) | 右填充 |
replace(str,substr1,substr2) | 用substr2替换substr1 |
- Utf8下一个英文字母一个字节,一个汉字3个字节
- SQL中索引从1开始
- 左填充
案例:查询邮箱的用户名
select substr(email,1,instr(email,'@')-1) 用户名;
4.1.2 数学函数
函数 | 说明 |
---|---|
round(数值) | 四舍五入 |
round(数值,n) | 保留n位小数 |
ceil(数值) | 向上取整 |
floor(数值) | 向下取整 |
truncate(数值,n) | 截断,取n位小数,小数位数小于n时返回原数值 |
mod(n1,n2) | 取余,被除数为负数,结果为负数;a-a/b*b |
rand() | 获取随机数,0-1之间的小数 |
4.1.3 日期函数
函数 | 说明 |
---|---|
now() | 返回当前系统日期+时间 |
curdate() | 返回当前系统日期3 |
curtime() | 返回当前系统时间 |
year(now()) | 返回当前系统的 年 |
year(‘日期’) | 返回日期的年份 |
month(‘日期’) | 数字 |
monthname(‘日期’) | 英文 |
日,时分秒都可以 | |
str_to_date() | 将日期格式的字符转换成指定格式的日期 |
date_format() | 将日期转换成字符 |
datediff(日期1,日期2) | 计算两个日期之间差的天数 |
4.1.4 流程控制函数
4.1.4.1 if 函数
- 语法
if(表达式1,表达式2,表达式3);
- 执行顺序
- 如果表达式1成立,返回表达式2的值,否则返回表达式3的值
4.1.4.2 Case 函数
(一) 相当于switch case,实现等值判断
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
......
else 要显示的值n或语句n;
end;#返回的是值
end case;# 返回的是语句
(二)相当于多重if,实现区间判断
case
when 条件1 then 要显示的值1或语句1;
when 条件1 then 要显示的值1或语句1;
......
else 要显示的值n或语句n;
end;#返回的是值
end case;# 返回的是语句
特点:
- 可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end中或之外
- 可以作为独立的语句使用,只能放在begin end 中
- when中的值或条件成立,则执行对应then后面的语句,并结束case;都不满足则执行else中的语句或值
- else语句可以省略,如果省略了else并且when中其他条件都不满足,则返回NULL
4.1.5 其他函数
函数 | 说明 |
---|---|
version() | 返回当前MySQL版本 |
database() | 返回当前使用的数据库 |
user() | 返回当前用户 |
md5(‘字符’) I 返回字符的md5形式
ifnull(字段,0);|判断某字段或表达式是否为NULL,为NULL则返回指定值,否则返回原数;
isnull(字段);| 判断某字段是否为NULL,是则返回1,否则返回0
4.2 多行函数
也被称为统计函数,聚合函数,组函数,分组函数
函数 | 说明 |
---|---|
Sum | 求和 |
Avg | 求平均 |
Max | 求最大值 |
Min | 求最小值 |
Count | 计数 |
- Sum,avg一般用于数值型,而max,min,count可以用于任何类型
- 所有分组函数都忽略null
- 可以与distinct搭配实现去重运算,
sum(distinct,字段);
- count函数
- Count(字段);统计字段中的个数
- Count(*) ; 可以用来计算总行数
- Count(1);相当于count(*),括号内可以是常量,结果一样;
- MyISAM存储引擎下,count(*)效率高;InnoDB存储引擎中count(*)与count(1)效率差不多,但是比count(字段)效率高
- 和分组函数一同查询的字段一般要求是group by后的字段
5. 分组查询
5.1 语法
# 语法:
select 分组函数,列 (要求出现在group by的后面)
from 表名
[where 条件]
group by 分组的列表
[having 分组后的筛选]
[order by 子句]
/*
注意:
查询列表必须特殊,要求是分组函数和 group by 后出现的字段
执行顺序5,1,2,3,4,6
*/
案例:查询每个工种的最高工资
5.2 分类
类别 | 数据源 | 位置 | 关键字 |
---|---|---|---|
分组前筛选 | 原始表 | group by 之前 | where |
分组后筛选 | 分组后结果 | group by 之后 | having |
- 分组函数作为条件肯定放在having子句中
- 能用分组前筛选的优先选择分组前筛选
- group by 子句支持单个字段分组,多个字段分组(多个字段分组时,就把多个字段都放在group by后面,表示多个字段一致时才分为一组),表达式或函数(用的比较少)
- 可以添加排序,需要放在整个分组查询的最后
5.2.1 添加分组前筛选
直接在group by 前加上where条件
案例:查询姓名中出现a字符的,每个部门的平均工资
5.2.2 添加分组后筛选
having 用于分组后的筛选
案例:查询哪个部门的员工数大于2,分两步走
- 计算每个部门的员工个数
- 利用第一步的结果求那个部门员工数大于2
6. 连接查询
也称为多表查询
数据:
boys:
beauty:
6.1 笛卡尔乘积现象
笛卡尔乘积现象:表1 有m行,表二有n行,结果有m*n行
原因:没有有效的连接条件
避免:添加合适的连接条件
通过beauty表中的boyfriend_id找到boys表中对应的名称
select name,boyName from beauty,boys;
这样写会出现笛卡尔乘积错误,因为没有加上任何连接条件,就会使得beauty中的每一条记录与boys中的每条记录匹配。如果beauty有12条,boys有4条,那么最后的出来的记录有48条
解决:添加合适的连接条件,让beauty表中的boyfriend_id与boys中的id相对应
select name,boyName from beauty,boys where beauty.boyfriend_id = boys.id;
6.2 连接查询分类
-
按年代分类
- sql92标准:MySQL中仅仅支持内连接
- sql99标准(推荐)MySQL中支持内连接+外连接(左外和右外)+交叉连接
-
按功能
- 内连接
- 等值连接
- 非等值连接
- 自连接
- 外连接
- 左外连接
- 右外连接
- 全外连接(MySQL不支持)
- 交叉连接
- 内连接
在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询。
- inner join(内连接,或等值连接):获取两个表中字段匹配关系的记录。
- left join(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
- right join(右连接): 与 left join 相反,用于获取右表所有记录,即使左表没有对应匹配的记录
6.3 SQL92——内连接
SQL92中MySQL只支持内连接
6.3.1 等值连接
6.3.1.1 案例
案例1:查询beauty表中女生对应boys表中的男朋友(girl.sql)
select name,boyName
from beauty,boys
where beauty.boyfriend_id = boys.id;
案例2:查询员工名、工种号、工种名(myemployees.sql)
select e.last_name,e.job_id,j.job_title
from jobs j,employees e
where e.job_id = j.job_id;
- 可以在from后为表起别名,减少复杂程度;起别名后原表名称不再起作用
- 表的顺序也可以改变
案例3:添加分组 查询每个城市的部门个数(myemployees.sql)
select count(*),city
from departments as d,locations as l
where d.location_id = l.location_id
group by city;
案例4:三表连接,查询员工名,部门名和所在的城市
select last_name,department_name,city
from employees e,departments d,locations l
where e.department_id = d.department_id
and d.location_id = l.location_id;
6.3.1.2 等值连接总结
- 等值连接的结果为多表连接的交集部分
- n表连接,至少需要n-1个连接条件‘
- 多表连接的顺序没有要求
- 一般需要为表起别名
- 可以搭配前面介绍的所有子句使用,比如排序,分组,筛选
6.3.2 非等值连接
连接条件不是等号
- 可以搭配前面介绍的所有子句使用,比如排序,分组,筛选
案例1:查询员工的工资和工资级别
工资级别是个区间范围
select salary,grade_level
from employees e,job_grades g
where salary between g.lowest_sal and g.highest_sal;
6.3.3 自连接
自己连接自己,不与其他表连接查询;所需要的信息都在同一张表中
案例1:查询员工名,及对应的上级名
把同一张表当作两张表进行处理,赋予别名以进行区分
select e.employee_id,e.last_name,m.employee_id,m.last_name
from employees as e,employees as m
where e.manager_id = m.employee_id;
6.4 SQL99
6.4.1 语法
select 查询列表
from 表1 别名
[连接类型] join 表2 别名
on 连接条件
[where 筛选条件]
[group by 分组]
[having 筛选条件]
[order by 排序列表]
连接类型
- 内连接:inner
- 外连接:
- 左外连接:left [outer]
- 右外连接:right [outer]
- 全外连接:full [outer] outer可省略
- 交叉连接:cross
6.4.2 内连接
6.4.2.1 等值连接
注意:
- 可以添加排序,分组,筛选等
- inner 可以省略
- 筛选条件放在where 后面,连接条件放在on后面,提高分离性,便于阅读
- inner join 连接与sql92的等值连接结果相同
- 内连接的结果是两个表的交集
案例1:查询员工名,部门名
select last_name,department_name
from employees e
inner join departments d
on e.department_id = d.department_id;
案例2:查询名字中包含e的员工名和工种名(添加筛选)
select last_name,job_title
from employees e
inner join jobs j
on e.job_id = j.job_id
where e.last_name like '%e%';
案例3:查询部门个数大于3的城市名和部门个数 (分组+筛选)
select city,count(*) as 部门个数
from departments d
inner join locations l
on d.location_id = l.location_id
group by city
having count(*) > 3;
案例4:查询员工名、部门名、工总名,并按部门排序 (三表连接)
select last_name,department_name,job_title
from employees e
inner join departments d
on e.department_id = d.department_id
inner join jobs j
on e.job_id = j.job_id
order by department_name desc;
6.4.2.2 非等值连接
案例1:查询员工的工资级别
select salary,grade_level
from employees e
join job_grades g
on e.salary between g.lowest_sal and g.highest_sal;
案例2:查询工资级别的个数>20的个数,并按工资级别降序
select count(*),grade_level
from employees e
join job_grades g
on e.salary between g.lowest_sal and g.highest_sal
group by grade_level
having count(*) > 20
order by grade_level desc;
6.4.2.3 自连接
案例:查询员工姓名及上级姓名
select e.last_name,e.manager_id,m.last_name,m.employee_id
from employees e
inner join employees m
on e.manager_id = m.employee_id;
6.4.3 外连接
应用场景:
- 查询一个表中有,另一个表中没有的情况
特点:
- 外连接的查询结果为主表中的所有记录
- 如果从表中有和他匹配的,则显示匹配的值
- 如果从表中没有和他匹配的,则显示NULL
- 外连接查询结果 = 内连接结果 + 主表中有 而没有的记录
- 左外连接,left join 左边的是主表
- 右外连接,right join右边的是主表
- 左外和右外交换两个表的顺序,可以实现同样的效果
- 全外连接 = 内连接 + 表1有表2无 + 表1无表二有;相当于并集
- 全外连接,两边都是主表
案例1:查询 男朋友 不在 boys表中 的 女神名
# 左外连接
select b.name
from beauty b
left outer join boys
on b.boyfriend_id = boys.id
where boys.id is null;
# 因为外连接查询结果 = 内连接结果 + 主表中有 而没有的记录 且boys表中的id为主键
# 右外连接
select b.name
from boys
right outer join beauty b
on b.boyfriend_id = boys.id
where boys.id is null;
# 因为外连接查询结果 = 内连接结果 + 主表中有 而没有的记录 且boys表中的id为主键
案例2:查询哪个部门没有员工
# 左外连接
select d.*,employee_id
from departments d
left outer join employees e
on d.department_id = e.department_id
where e.employee_id is null;
# 右外连接
select d.*,employee_id
from employees e
right outer join departments d
on d.department_id = e.department_id
where e.employee_id is null;
6.4.4 交叉连接
就是beauty表与boys表的笛卡尔乘积
select b.*,boys.*
from beauty b
cross join boys
7. 子查询
7.1 概述
7.1.1 相关定义
出现在其他语句中的select语句,称为子查询或内查询
内部嵌套其他select语句的查询,称为主查询或外查询
7.1.2 分类
按子查询出现的位置
- select 后面:仅仅支持标量子查询
- from 后面 :支持表子查询
- where 或 having 后面 : 支持标量子查询、列子查询、行子查询(较少)
- exists 后面 (相关子查询):支持表子查询
按结果集的行列数不同
- 标量子查询(结果集只有一行一列,单行)
- 列子查询(一列多行,多行)
- 行子查询(一行多列)
- 表子查询(一般为多行多列)
7.2 where或having后面
特点
- 子查询放在小括号内
- 子查询一般放在条件的右侧
- 标量子查询,一般搭配着单行操作符(< > >= <= <> =)使用
- 列子查询,一般搭配多行操作符(in,any/some,all)使用
- 子查询的执行优先于主查询
7.2.1 标量子查询(单行子查询)
案例1:谁的工资比Abel高?
第一步:查询Abel的工资
# 一行一列
select salary
from employees
where last_name = 'Abel';
第二步:查询员工信息,满足salary > 第一步结果
select *
from employees
where salary > (
select salary
from employees
where last_name = 'Abel'
);
案例2:返回job_id与141号员工相同,且salary比143号员工多的 员工姓名,job_id和工资
第一步:查询141号员工的job_id
# 一行一列
select job_id
from employees
where employee_id = 141;
第二步:查询143号员工的salary
# 一行一列
select salary
from employees
where employee_id = 143;
第三步:查询job_id与141号员工相同,且salary比143号员工多的 员工姓名,job_id和工资
select last_name,job_id,salary
from employees
where job_id = (
select job_id
from employees
where employee_id = 141
) and salary > (
select salary
from employees
where employee_id = 143
);
案例3:返回公司工资最少的员工的last_name,job_id,salary
第一步:查询公司的 最低工资
select min(salary)
from employees;
第二步:查询工资等于最低工资的员工的last_name,job_id,salary
select last_name,job_id,salary
from employees
where salary = (
select min(salary)
from employees
);
案例4:查询最低工资大于 50号部门最低工资 的 部门id和其最低工资
第一步:查询50号部门的最低工资
select min(salary)
from employees
where department_id = 50;
第二步:查询每个部门的最低工资
select min(salary)
from employees
group by department_id;
第三步:对第二步的结果进行筛选,满足salaly > 第一步
select min(salary)
from employees
group by department_id
having min(salary) > (
select min(salary)
from employees
where department_id = 50
);
7.2.2 列子查询(多行子查询)
- 返回多行
- 使用多行比较符
- 也可以把in替换成 =any not in 替换为<> all
操作符 | 含义 |
---|---|
in/not in | 等于列表中的任意一个,用的比较多 |
any / some | 和子查询返回的某一个值比较,可以用min/max代替 |
all | 和子查询返回的所有值比较,可以用max/max代替 |
案例1:返回location_id是1400或1700的部门的所有员工姓名
第一步:查询部门location_id 为1400 或 1700的部门
# 一列多行
select distinct department_id
from departments
where location_id in (1400,1700);
第二步:查询员工姓名,要求部门号是第一步中的某一个
select last_name
from employees
where department_id in ( # 也可以把in替换成 =any not in 替换为<> all
select distinct department_id
from departments
where location_id in (1400,1700)
);
案例2:返回其他工种中比 IT_PROG 工种任意工资低的 员工的员工号,姓名,job_id,salay
使用ANY
第一步:查询IT_PROG 工种的工资
select salary
from employees
where job_id = 'IT_PROG';
第二步:查询工资比 IT_PROG 工种的任意工资 低的员工的相关信息
select employee_id,last_name,job_id,salary
from employees
where salary < any(
select salary
from employees
where job_id = 'IT_PROG'
) and job_id <> 'IT_PROG';
用max代替,相当于标量子查询
select employee_id,last_name,job_id,salary
from employees
where salary < (
select max(salary)
from employees
where job_id = 'IT_PROG'
) and job_id <> 'IT_PROG';
7.2.3 行子查询(一行多列)
此情况用的比较少
案例:查询员工编号最小并且工资最高的员工信息
select *
from employees
where (employee_id,salary) = (
select min(employee_id),max(salary)
from employees
);
7.3 select后面的子查询
仅仅支持标量子查询
案例:查询每个部门的员工个数
select d.*,(
select count(*)
from employees e
where e.department_id = d.department_id
) 个数
from departments d;
员工个数为0的部门也能求出来,如果使用group by 只能求出员工个数不为0的部门
7.4 from后面的子查询
将子查询结果充当一张表,要求必须起别名,否则找不到
案例:查询每个部门的平均工资的工资等级
第一步:查询每个部门的平均工资
select department_id,avg(salary)
from employees
group by department_id;
第二步:连接第一步结果和job_grades表,筛选条件:平均工资 between lowest_sal and highest_sal
select avg_dep.*,grade_level
from (
select department_id,avg(salary) as avg
from employees
group by department_id
) avg_dep
inner join job_grades g
on avg_dep.avg between lowest_sal and highest_sal;
7.5 exists 后面(相关子查询)
语法:
select exists(完整的查询语句);
- 结果:1或0,查看子查询语句是否有数据
案例:查询有员工的部门名
select department_name
from departments d
where exists(
select *
from employees e
where d.department_id = e.department_id
);
使用in,not in 为没有员工的部门名
select department_name
from departments d
where department_id in(
select department_id
from employees
);
7.6 案例
案例1:查询各部门中工资比本部门平均工资高的员工的员工号,姓名,工资,部门
第一步:查询本部门的平均工资
select avg(salary) as ag,department_id
from employees e
group by department_id;
第二步:连接第一步结果集和employees表,进行1筛选
select employee_id,last_name,salary,e.department_id
from employees e
inner join (
select avg(salary) as ag,department_id
from employees e
group by department_id
) as ag_dep
on e.department_id = ag_dep.department_id
where salary > ag_dep.ag;
案例2:查询 和 姓名中包含字母u的员工 在同一部门的员工的员工号和姓名
第一步:查询姓名中包含字母u的员工所在的部门
# 去重
select distinct department_id
from employees
where last_name like '%u%';
第二步:查询部门号等于第一步中任意一个的员工号和姓名,并去除名字中带u的
select employee_id,last_name
from employees
where department_id in (
select distinct department_id
from employees
where last_name like '%u%'
) and last_name not like '%u%';
案例3:查询部门 在location_id为1700的 部门 工作的员工的员工号
第一步:查询location_id为1700的部门
select distinct department_id
from departments
where location_id = 1700;
第二步:筛选
select employee_id
from employees e
where e.department_id in ( # in 也可以写成 =any
select distinct department_id
from departments
where location_id = 1700
);
8. 分页查询
8.1 语法
select 查询列表
from 表
[join type join 表2]
[on 连接条件]
[where 筛选条件]
[group by 分组字段]
[having 分组后筛选]
[order by 排序字段]
limit offset,size;
/*
offset:要显示条目的起始索引(从0开始)
size:要显示的条目个数
要显示的页数:page ,每页显示的条目个数size
limit (page-1)*size,size
*/
8.2 案例
案例1:查询前5条员工信息
# 0可以省略
select * from employees limit 0,5;
案例2:有奖金的员工信息,并显示工资前10名
select *
from employees
where commission_pct is not null
order by salary desc
limit 0,10;
9. 联合查询
-
应用场景:
- 要查询的结果来自于多张表,且多个表没有直接的连接关系,但查询信息一致时使用
-
作用
- 将多条查询语句的结果合并为一个结果
-
语法:
查询语句1 union 查询语句2 ...
案例:查询部门编号 > 90或邮箱包含a的员工信息
# 联合查询
select * from employees where department_id > 90
union
select * from employees where email like '%a%';
10. 各语句执行顺序
每执行一步都会生成一个虚拟的表格
语句 | 执行顺序 |
---|---|
select 查询列表 | 7 |
from 表 | 1 |
join type join 表2 | 2 |
on 连接条件 | 3 |
where 筛选条件 | 4 |
group by 分组字段 | 5 |
having 分组后筛选 | 6 |
order by 排序字段 | 8 |
limit offset,size | 9 |
三、DML(Data Manipulate Language)数据操作语言
1. 插入语句
# 方式一
insert into 表名(列名,...)
values(值1,...),
(值2,...)
...;
注意
- 插入的值的类型要与列的类型一致或兼容(可以隐式转换)
- 值可以为空的列的填写方式
- 列写,值用NULL
- 列与值都不写
- 列的顺序可以调换,但是需要保证插入的值与列一致
- 可以省略列名,默认为所有列,且列的顺序与表中一致
# 方式二
insert into 表名
set 列名1 = 值1,列名2 = 值2...;
两种方式比较
- 方式一支持多行插入,方式二不支持
- 方式一支持子查询,方式二不支持
insert into table1(...)
select ... ;
# 相当于把select子查询得到的结果集插入到对应列
2. 修改语句
2.1 修改单表的记录
语法
update 表名
set 列名1 = 新值1,列名2 = 新值2...
where 筛选条件;
- 不加筛选条件则修改所有表
- 客户端启动时可加上参数:mysql -u root --safe-updates -p 如果数据更新不加where会报错
2.2 修改多表的记录
语法
# sql92 只支持内联
update table1 别名,table2 别名
set 列1 = 值1,...
where 连接条件
and 筛选条件;
# sql99
update table1 别名
inner|left|right join table2 别名
on 连接条件
set 列1 = 值1,...
where 筛选条件;
3. 删除语句delete命令
3.1 单表删除
# 方式一
delete from 表名 [where 条件];
- 不加条件会删除表中所有数据
- truncate命令
# 方式二
truncate table 表名;
- 清空表中所有记录,相当于delete from 表名;
3.2 多表删除
# sql92
delete 删除表的别名
from table1 别名,table2 别名
where 连接条件
and 筛选条件;
/*
别名可以不起
删除哪个表的记录就在delete 后写哪个
*/
# sql99
delete 删除表的别名
from table1 别名
inner|left|right join table2 别名
on 连接条件
where 筛选条件;
/*
3.3 delete 与 truncate 区别
- delete 可以加where条件,truncate不能加
- truncate删除,效率高一点
- 如果要删除的表中有自增长列,delete删除之后再插入数据,自增长列的值从断点开始;而truncate删除后,自增长列从1开始
- truncate删除没有返回值,delete有返回值
- truncate删除不能回滚,delete删除可以回滚
- truncate table 表名 不会写入日志,直接删除,delete from 表名,会有日志。
四、DDL(Data Define Languge)数据定义语言
1. 数据库的管理
1.1 创建库
create database 数据库名;
# 容错性处理,如果库不存在就创建,存在也不会报错
create database if not exists 数据库名;
1.2 修改库
# 更改数据库的字符集
alter database 数据库名 character set gbk;
1.3 删除库
drop database 数据库名;
# 容错性处理
drop database if exists 数据库名;
2. 表的管理
2.1 创建表
create table 表名(
列名1 列的类型 [(长度) 约束],
列名2 列的类型 [(长度) 约束],
...
列名n 列的类型 [(长度) 约束]
);
案例:
create table student(
id int auto_increment,
name varchar(64) not null,
primary key(id)
)engine = InnoDB default charset = utf8;
- 字段可以设置为不为空,即not null;
- auto_increment 定义列为自增的属性,一般用于主键,数值会自动加1;
- primary key 关键字用于定义列为主键。 可以使用多列来定义主键,列间以逗号分隔。
- engine 设置存储引擎,charset 设置编码。此句可不写
2.2 修改表
语法:
alter table 表名 add|drop|modify|change 列名 [列类型 约束];
-
修改字段名
alter table 表名 change column 原字段 新字段 新字段属性; #column 可以省略
-
修改字段属性
alter table 表名 modify 字段 字段属性;
-
增加字段
alter table 表名 add 字段 字段属性 (after 字段);
-
删除字段
alter table 表名 drop 字段;
-
重命名表名
alter table 原表名 rename 新表名;
2.3 删除表
drop table 表名;
drop table if exists 表名;
2.4 复制表
复制表结构
- 复制表的所有结构
create table 新表 like 表1
- 复制部分结果,不复制数据
create table 新表 select 想要复制的字段 from 表1 where 0;
复制表结构与数据
- 复制全部数据
create table 新表 select * from 表1; # 部分数据需要加条件
- 复制部分数据
create table 新表 select 需要的字段 from 表1 where 筛选条件
3. MySQL基础数据类型
3.1 数字型
原则:所选择的类型越简单越好,能保存数值的类型越小越好
类型 | 大小 | 范围(有符号) | 范围(无符号) | 用途 |
---|---|---|---|---|
tinyint | 1字节 | -128-+127 | 0-255 | 最小整数 |
int | 4字节 | -2147483648-+2147483647 | 大整数值 | |
float(m,n) | 4-8个字节 | 单精度浮点型(浮点数) | ||
double(m,n) | 8个字节 | 双精度浮点型(浮点数) | ||
decimal(m,n) | 变长 | 浮点数(更加精确,长度一般无限制) |
3.2 字符型
类型 | 大小 | 用途 |
---|---|---|
char(M) | 0-255字节 | 存储定长的字符串,M默认为1;耗费空间但效率高 |
varchar(M) | 0-65535字节 | 存储变长的字符串,无默认值;节省空间但效率低 |
text | 0-65535字节 | 长文本数据 |
blob | 0-65535字节 | 二进制文本(不建议) |
enum(‘w’,‘m’) | 65535个成员 | 枚举 |
set(‘w’,‘m’) | 64个成员 | 集合 |
3.3 时间型
类型 | 大小 | 范围 | 格式 | 用途 |
---|---|---|---|---|
date | 3字节 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
time | 3字节 | -838:59:59/838:59:59 | HH:MM:SS | 时间值 |
year | 4字节 | 1901/2155 | YYYY | 年份值 |
datetime | 8字节 | 1000-01-01 00:00:00 / 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合时间和日期 |
timestamp | 4字节 | 1970-2038年的某个时刻 | 日期+时间 |
- timestamp支持的时间范围较小
- timestamp与实际时区有关,更能反应实际的日期,而datetime只能反映出插入时的当地时区
- timestamp的属性受MySQL版本和SQLMode的影响很大
显示当前时区
show variables like 'time_zone';
设置时区
set time_zone='+9:00';# 东9区
4. 常见约束
对表中的数据进行限制,来保证表中的数据准确可靠
create table 表名(
字段名 字段类型 列级约束,
字段名 字段类型,
表级约束
);
4.1 六大约束
约束 | 说明 |
---|---|
not null | 非空,用于保证该字段的值不能为空 |
default | 默认,保证该字段有默认值 |
primary key | 主键,保证该字段值的唯一性,且不为空 |
unique | 唯一,保证该字段的唯一性,可以为空,但只能有一个 |
check | 检查约束(MySQL不支持) |
foreign key | 外键,用于限制两个表的关系,用于保证该字段的值必须来自主表的关联列的值,在从表添加外键约束,用于引用主表在某列的值 |
4.2 添加时机与分类
添加约束的时机
- 创建表
- 修改表
约束的添加分类:
- 列级约束:六大约束语法上都支持,但外键约束没有效果
- 直接在字段名和类型后面追加约束类型
- 不可以起约束名
- 表级约束:六大约束除了非空、默认都可以
- 在字段的最下面
[constraint 约束名] 约束类型(字段名)
- 可以起约束名
4.2.1 创建表时添加约束
4.2.1.1 添加列级约束
create table stuinfo(
id int primary key, # 主键
stuName varchar(20) not null, # 非空
gender char(1) check(gender='男' or gender='女'), # 检查
seat int unique, # 唯一
age int default 18, # 默认
majorId int references major(id) # 外键(不起作用)
);
create table major(
id int primary key,
majorName varchar(20)
);
#查看表中的索引
show index from 表名;
4.2.1.2 添加表级约束
create table stuinfo(
id int,
stuName varchar(20),
gender char(1),
seat int,
age int,
majorId int,
# 表级约束,不支持默认与非空
constraint pk primary key(id), #主键
unique(seat)# 唯一
foreign key(majorId) references major(id) # 外键
);
4.2.2 修改表时添加约束
添加列级约束。可添加项:默认,非空
alter table 表名 modify 字段 字段属性 约束;
添加表级约束。可添加项:主键,唯一,外键
alter table 表名 add 约束(字段) [外键的引用];
4.2.3 修改表时删除约束
删除非空约束
alter table 表名 modify 字段 字段属性 null;
删除默认约束
alter table 表名 modify 字段 字段属性;
删除主键
alter table 表名 drop primary key;
删除唯一
alter table 表名 drop union index 字段;
删除外键
alter table 表名 drop foreign key 外键名;
4.3 主键与唯一的区别
区别 | 主键 | 唯一 |
---|---|---|
是否唯一 | 是 | 是 |
是否可以为空 | 否 | 是,但只能有一个非空 |
个数 | 一个 | 可以多个 |
是否允许组合(不推荐) | 是(联合主键) | 是 |
4.4 外键的特点
- 要求在从表设置外键关系
- 从表的外键列的类型和主表的关联列的类型要求一致或者兼容,名称无要求
- 主表的关联列必须是一个key(一般是主键或者唯一)
- 插入数据时,先插入主表,再插入从表;删除数据时,先删除从表,再删除主表
5. 标识列
标识列也称为自增长列:可以不用手动的插入值,系统提供默认的序列值;关键字: auto_increment
- 必须与key进行搭配,一般和主键搭配,唯一也可以
- auto_increment 的步长可以设置
- 最多有一个标识列
5.1 创建表时设置标识列
auto_increment 定义列为自增的属性,一般用于主键,数值会自动加1;
create table student(
id int primary key auto_increment,
name varchar(64) not null
);
5.2 修改表时设置标识列
alter table 表名 modify 字段名 int primary key auto_increment;
5.3 修改表时删除标识列
alter table 表名 modify 字段名 int primary key;
5.4 设置步长及起始值
执行下面语句
show variables like '%auto_increment%';
其中auto_increment_increment 为步长可以设置
set auto_increment_increment = 3;# 步长设置为3
auto_increment_increment 为起始值,MySQL中不支持设置,设置没有效果;可以在想修改起始值的地方手动插入,比如表的第一行插入为10,之后的数据都会在10的基础上进行自增长
五、TCL(Transaction Control Language)事务控制语言
事务是由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条SQL语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事务开始以前的状态,如果单元中的所有SQL语句均执行成功,则事务被顺利执行
1. 存储引擎了解
-
在MySQL中的数据用各种不同的技术存储在文件(或内存)中。
-
查看MySQL支持的存储引擎
show engines;
-
MySQL中用的最多的存储引擎有:innodb,MyISAM,MEMORY等,其中innodb支持事务;MyISAM,MEMORY等不支持事务
2. 事务的ACID特性
- 原子性(Atomicity)
- 是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生;
- 一致性(Consistency)
- 事务必须使数据库从一个一致性状态变换到另一个一致性状态。
- 隔离性(Isolation)
- 事务的隔离性是指一个事务的执行不能够被其他事务打扰。即一个事务内部的操作及使用的数据对并发的其他事务时隔离的,并发执行的各个事务之间不能相互干扰。
- 持久性(Durability)
- 持久性是指一个事务一旦被提交,则会永久的改变数据库的数据,接下来的其他操作和数据库故障不应该对其有任何影响。
3. 事务的创建
3.1 分类
分类
- 隐式事务:事务没有明显的开启和结束的标记
- 比如insert,update,delete语句
- 显式事务:事务具有明显的开启和结束的标记
- 前提:必须先设置自动提交功能为禁用
# 自动提交功能,默认开启
show variables like 'autocommit';
# 关闭自动提交功能,只针对当前会话有效
set autocommit = 0;
3.2 创建步骤
步骤1:开启事务
set autocommit = 0;
start transaction;可选
步骤2:编写一组事务的SQL语句(增删改查:select insert update delete)而DDL语言没有事务之说
SQL语句1;
savepoint a;
# 可选,设置保持点。a为节点名
# 搭配rollback使用,若回滚到保持点a,则会恢复保持点a以下的语句
SQL语句2;
...
步骤3:结束事务
commit; # 成功时执行 提交事务
rollback;# 出错时执行 回滚事务,得联合应用程序
rollback to a;# 回滚到保持点a
4. 数据库的隔离级别
4.1 并发问题
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
- 脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有被提交的字段。之后,若T2回滚,T1读取的内容就是临时且无效的
- 不可重复读:对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段。之后,T1再次读取同一个字段,值就不同了
- 幻读:对于两个事务T1,T2,T1读取了一个字段,然后T2在该表中插入了一些新的行。之后,T1再次读取同一个表,就会多出几行。
4.2 数据库事务的隔离级别
数据库事务的隔离性:数据库系统必须具有隔离并发运行各事务的能力,使他们不会相互影响,避免各种并发问题。
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
隔离级别 | 描述 |
---|---|
READ UNCOMMITTED(读未提交数据) | 允许事务读取未被其他事务提交的变更。脏读、不可重复读、幻读的问题都会出现 |
READ COMMITED(读已提交数据) | 只允许事务读取已经被其他事务提交的变更。可以 避免脏读,但不可重复读和幻读问题仍可能存在 |
REPEATABLE READ(可重复读) | 确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新。可以避免脏读和不可重复读,但幻读问题仍会出现 |
SERIALIZABLE(串行化) | 确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有并发问题都可以避免,但性能十分低下 |
- Oracle 支持2种事务隔离级别:READ COMMITED,SERIALIZABLE。Orcale默认的事务隔离级别为READ COMMITED
- MySQL支持4种事务隔离级别。默认为REPEATABLE READ
查看默认事务隔离级别
select @@tx_isolation;
# 或 8.0版本以上
select @@transaction_isolation;
设置当前隔离级别
set session transaction isolation level 隔离级别;
设置全局隔离级别
set global session transaction isolation level 隔离级别;
六、视图
1. 概述
1.1 含义
MySQL5.0.1 版本出现的新特性。一种虚拟存在的表,行和列的数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的,只保存了SQL逻辑,不保存查询结果。
1.2 应用场景
- 多个地方用到同样的查询结果
- 该查询结果使用的SQL语句较复杂
1.3 视图好处
- 重用SQL语句
- 简化复杂的SQL操作,不必知道查询细节
- 保护数据,提高安全性
2. 创建视图
2.1 语法
create view 视图名
as
查询语句;
2.2 案例
案例1:查询姓名中包含a字符的员工名,部门名和工种信息
# 创建视图 myv1
create view myv1
as
select last_name,department_name,job_title
from employees e
inner join departments d on e.department_id = d.department_id
inner join jobs j on j.job_id = e.job_id;
# 使用视图
select * from myv1 where last_name like '%a%';
3. 修改视图
方式一
如果视图存在就进行修改,不存在则创建
create or replace 视图名
as
查询语句;
方式二
使用关键字alter
alter view 视图名
as
查询语句;
4. 删除视图
drop view 视图名1,视图名2,...;
5. 查看视图结构
desc 视图名; # 视图结构
show create view 视图名;# 查看建表语句
6. 视图数据更新
对视图的更改会作用到原表,一般会对视图进行权限限制
6.1 插入数据
insert into 视图名(列名,...)
values(值1,...),
(值2,...)
...;
插入的数据会直接作用到原表,有些视图数据不能够进行插入
6.2 修改数据
update 视图名
set 列名1 = 新值1,列名2 = 新值2...
where 筛选条件;
6.3 删除数据
delete from 表名 where 条件;
6.4 不允许更新视图的情况
- 包含以下关键字的SQL语句:分组函数,distinct,group by ,having,union,union all
- 常量视图
- select中包含子查询
- join
- from一个不能更新的视图(视图可以嵌套)
- where 子句的子查询引用了from子句中的表
7. 视图与表的对比
xx | 创建语法的关键字 | 是否实际占用了物理空间 | 使用 |
---|---|---|---|
视图 | create view | 保存了SQL逻辑 | 增删改查,一般不能增删改 |
表 | create table | 保存了数据和逻辑 | 增删改查 |
七、变量
1. 分类
- 系统变量
- 全局变量:global
- 会话变量:session
- 自定义变量
- 用户变量
- 局部变量
2. 系统变量
- 变量由系统提供,属于服务器层面。
- 默认为session,可写可不写。
- 服务器每次启动将为所有的全局变量赋初值,针对于所有的会话(连接)有效,但是不能跨重启(每次启动会恢复到系统配置的默认值)。
- 会话变量针对于当前对话(连接)有效
查看所有系统变量
show global | [session] variables;
查看满足条件的部分系统变量
show global | [session] variables like '%char%';
查看指定的某个系统变量的值
select @@global | [session] .系统变量名;
为某个系统变量赋值
# 语法1
set global | [session] 系统变量名 = 值;
# 语法2
set @@global | [session] .系统变量名 = 值;
3. 自定义变量
- 由用户自己定义
- 使用步骤:声明,赋值,使用(查看,比较,运算等)
3.1 用户变量
- 作用域:针对当前会话(连接)有效
- 可以放在任何地方,begin end 内部或者外部
声明并初始化
set @用户变量名 = 值;
set @用户变量名 := 值; # 推荐
select @用户变量名=值;
赋值(更新用户变量的值)
# 方式一:利用声明并初始化操作
set @用户变量名 = 值;
set @用户变量名 := 值;
select @用户变量名=值;
# 方式二:通过select into,把表中数据复制给变量
select 字段1,字段2 ... into @变量名1, @变量名2 ...
from 表名;
使用(查看用户变量的值)
select @用户变量名;
案例:使用用户变量
set @m = 1;
set @n = 2;
set @sum = @m + @n;
select @sum;
3.2 局部变量
- 仅仅在定义它的begin end中有效
- 应用于begin end中的第一句话
声明
declare 局部变量名 类型;
declare 局部变量名 类型 default 值;
赋值
# 方式一:通过set或select
set 局部变量名 = 值;
set 局部变量名 := 值;
select @局部变量名=值;
# 方式二:通过select into
select 字段1,字段2... into 局部变量名1,局部变量名2...
from 表名;
使用
select 局部变量名;
八、存储过程和函数
1. 存储过程
1.1 含义
一组预先编译好的SQL语句的集合,理解为批处理语句
- 提高代码的复用性
- 简化操作
- 减少编译次数并且减少了和数据库服务器的连接次数,提高了效率
1.2 创建
语法:
create procedure 存储过程名(参数列表)
begin
存储过程体(一组合法的SQL语句)
end
注意
-
参数列表由三部分组成:参数模式 参数名 参数类型
# 举例 in stuName varchar(20)
参数模式 | 说明 |
---|---|
in | 作为输入,该参数需要调用方传入值 |
out | 作为输出,该参数可以作为返回值 |
inout | 可以作为输入或输出,既可以作为传入值,也可以作为返回值 |
- 存储过程体
- 如果存储过程体仅有一句话,begin end 可以省略
- 存储过程体中的每条SQL语句的结尾必须加分号
- 存储过程的结尾可以使用delimiter 重新设置
- 语法:
delimiter 结束标记
- 语法:
1.3 调用
语法:
call 存储过程名(实参列表);
1.4 删除存储过程
- 一次只能删除一个,不能一次删除多个
- 不能修改,只能删除后重新创建
drop procedure 存储过程名;
1.5 查看存储过程信息
不能使用desc 存储过程名。
show create procedure 存储过程名;
1.6 案例
1.4.1 空参列表
案例1:插入到account表中5条记录
步骤1:创建存储过程myp1,并把结束标记改为$
delimiter $
create procedure myp1()
begin
insert into account(username,money)
values('employee1','2000'),
('employee2','3000'),
('employee3','4000'),
('employee4','5000'),
('employee5','6000');
end $
步骤2:调用存储过程体myp1
call myp1()$
步骤3:查看account表中的记录
select * from account$
1.6.2 创建带in模式参数的存储过程
案例1:(单变量输入)根据女生名 查询 其对应的男朋友信息(数据库文件:girl.sql)
步骤1:创建存储过程myp1,并把结束标记改为$
delimiter $
create procedure myp1(in beautyName varchar(20))
begin
select bo.*
from boys bo
right join beauty b on bo.id = b.boyfriend_id
where b.name = beautyName;
end $
步骤2:调用存储过程体myp1
call myp1('赵敏')$
案例2:(多变量输入)查看account表中员工名与他的钱是否一致
delimiter $
create procedure myp2(in username varchar(20),in money varchar(10))
begin
declare res int default 0;# 声明并初始化
select count(*) into res # 给变量赋值
from account
where account.username = username
and account.money = money;
# 使用,如果得到res 为0说明没有匹配不上,如果res > 0说明正确
select if(res,'正确','错误');
end $
调用
call myp2('boss','1000');
1.6.3 创建带out模式的存储过程
案例1:根据女神名返回对应其男朋友姓名
delimiter $
create procedure myp2(in beautyName varchar(20),out boyName varchar(20)) # 自动返回boyName
begin
select bo.boyName into boyName #赋值给boyName
from boys bo
inner join beauty b
on b.boyfriend_id = bo.id
where b.name = beautyName;
end $
调用
# 定义一个用户变量接收返回值
set @bName$
call myp2('赵敏',@bName)$
select @bName$
1.6.4 创建带inout模式的存储过程
案例:传入a,b两个数,返回他们的2倍
delimiter $
create procedure myp3(inout a int,inout b,int)
begin
set a = 2 * a;
set b = 2 * b;
end $
调用
# 创建两个用户变量
set @m = 10$
set @n = 20$
call myp3(@m,@n)$
select @m,@n$
2. 函数
2.1 函数与存储过程的区别
- 函数只能有一个返回:适合处理数据后返回一个结果
- 存储过程可以有0个或多个:批量插入、批量更新;适合增删改
2.2 创建函数
语法:
create function 函数名(参数列表) returns 返回类型
begin
函数体
return 值;
end
- 参数列表包含两部分:参数名 参数类型
- 肯定有return语句,没有会报错
- 如果return语句没有放在函数体最后也不报错,但是不建议
- 函数体中仅有一句话可以省略begin end
- 可以使用delimiter语句设置结束标记
2.3 调用函数
select 函数名(实参列表)
2.4 查看函数
show create function 函数名;
2.5 删除函数
drop function 函数名;
2.6 案例
2.6.1 无参有返回
案例:返回公司的员工个数
delimiter $
create function myf1() returns int
begin
declare c int default 0; #定义局部变量作为返回值
select count(*) into c # 为变量赋值
from employees;
return c;
end $
调用
select myf1()$
2.6.2 有参有返回
案例:根据员工名返回工资
delimiter $
create function myf2(eName varchar(20)) returns double
begin
declare money double default 0;# 定义局部变量
# set @money := 0; #定义用户变量,使用时变量名前需要加 @
select salary into money
from employees
where last_name = eName;
return money;
end $
调用
select myf2('Baer')$
3. 查看MySQL中的存储过程和函数
用户创建的存储过程和函数可以在mysql.sql中的proc表中查看
select db,name,type from mysql.proc;
九、流程控制结构
- 顺序结构:程序从上往下执行
- 分支结构:程序从两条或多条路径中选择一条去执行
- 循环结构:程序在满足一定条件的基础上,重复执行一段代码
1. 分支结构
1.1 if函数
- 语法
if(表达式1,表达式2,表达式3);
- 执行顺序
- 如果表达式1成立,返回表达式2的值,否则返回表达式3的值
1.2 case结构
(一) 相当于switch case,实现等值判断
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
......
else 要显示的值n或语句n;
end;#返回的是值
end case;# 返回的是语句
(二)相当于多重if,实现区间判断
case
when 条件1 then 要显示的值1或语句1;
when 条件1 then 要显示的值1或语句1;
......
else 要显示的值n或语句n;
end;#返回的是值
end case;# 返回的是语句
特点:
- 可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end中或之外
- 可以作为独立的语句使用,只能放在begin end 中
- when中的值或条件成立,则执行对应then后面的语句,并结束case;都不满足则执行else中的语句或值
- else语句可以省略,如果省略了else并且when中其他条件都不满足,则返回NULL
案例:创建存储过程,根据传入的成绩来显示等级;90-100:显示A ;80-90:显示B;60-80:显示C;其他:显示D
delimiter $
create procedure test_case(in score int)
begin
# 区间判断
case
when score >= 90 and score <= 100 then select 'A';
when score >= 80 then select 'B';
when score >= 60 then select 'C';
else select 'D';
end case; # 执行的是语句,所以使用end case
end $
调用
call test_case(99)$
1.3 if结构
语法:(只能在begin end中使用)
if 条件1 then 语句1;
elseif 条件2 then 语句2;
elseif 条件3 then 语句3;
...
[else 语句n;]
end if;
案例:创建函数,根据传入的成绩来显示等级;90-100:返回A ;80-90:返回B;60-80:返回C;其他:返回D
delimiter $
create function test_if(score int) returns char
begin
if score >= 90 and score <= 100 then return 'A';
elseif score >= 80 then return 'B';
elseif score >= 60 then return 'C';
else return 'D';
end if;
end $
调用
select test_if(55)$
2. 循环结构
分类:while,loop,repeat
循环控制
- iterate:类似于continue ,继续,结束本次循环,继续下一次
- leave:类似于break,跳出,结束当前所在循环
2.1 while
语法:
[标签:] while 循环条件 do
循环体;
end while [标签];
案例:批量插入,根据次数插入到account 表中多条记录
create procedure pro_while(in insertCount int)
begin
declare i int default 1; #定义循环因子
while i <= insertCount do
insert into account(username,money) values(concat('employee',i),'10000');
set i = i + 1;
end while;
end $
调用
call pro_while(100)$
案例:批量插入,根据次数插入到account 表中多条记录
使用leave ,当次数大于20时停止,此时需要添加标签
create procedure pro_while(in insertCount int)
begin
declare i int default 1; #定义循环因子
a:while i <= insertCount do
insert into account(username,money) values(concat('boss',i),'10000');
if i >= 20 then leave a;
end if;
set i = i + 1;
end while a;
end $
案例:批量插入,根据次数插入到account 表中多条记录
使用iterate ,当次数大于20时停止,此时需要添加标签
create procedure pro_while(in insertCount int)
begin
declare i int default 0; #定义循环因子
a:while i <= insertCount do
set i = i + 1;
if mod(i,2) != 0 then iterate a;
end if;
insert into account(username,money) values(concat('manager',i),'10000');
end while a;
end $
2.2 loop
语法:
[标签:] loop
循环体;
end loop [标签];
没有循环条件,可以用来模拟简单的死循环
2.3 repeat
语法:类似于do…while
[标签:] repeat
循环体;
untile 循环结束条件
end repeat [标签];