一:SQL
DDL(Data Definition Language):数据定义语言,用来定义数据库对象(数据库,表,字段)
DML(Data Manipulation Language):数据操作语言,用来对数据库表中的数据进行增删改
DQL(Data Query Language):数据查询语言,用来查询数据库中的数据
DCL(Data Control Language):数据控制语言,用来创建数据库用户、控制数据库的访问权限
1: DDL
1.1. 数据库操作
-
查询
sql# 查询所有数据库 show databases; # 查询当前数据库 select database();
-
创建
sqlcreate database [if not exists] 数据库表名 [default charset 字符集][collate 排序规则];
-
删除
sqldrop database [if not exists] 数据库名;
-
使用
sqluse 数据库名;
1.2. 表操作
-
查询
sql# 查询当前数据库所有表 show tables; # 查询表结构 desc 表名; # 查询指定表的创建表语句 show create table 表名;
-
创建
sqlcreate table 表名( 字段名 字段类型 [comment '注释'] )[comment '注释']
-
修改
sql# 修改数据类型 alter table 表名 modify 字段名 新数据类型[长度]; # 修改字段名和数据类型 alter table 表名 change 字段名 新字段名 类型[长度] [comment '注释'][约束]; # 添加字段 alter table 表名 add 字段名 类型[长度] [comment '注释'][约束]; # 删除字段 alter table 表名 drop 字段名; # 修改表名 alter table 表名 rename to 新表名;
-
删除
sql# 删除表 drop table [if exists] 表名; # 删除指定表,并重新创建该表 truncate table 表名;
2: DML
2.1. 添加数据
sql
# 给指定字段添加数据
insert into 表名(字段名1,字段名2,...) values(值1,值2,...);
# 给全部字段添加数据
insert into 表名 values(值1,值2,...);
# 批量添加数据
insert into 表名 values(值1,值2,...),(值1,值2,...);
insert into 表名(字段名1,字段名2,...) values(值1,值2,...),(值1,值2,...);
注意:
- 插入数据时,指定的字段顺序需要与值的顺序是一一对应的
- 字符串和日期型数据应该以单引号引起来
- 插入的数据大小,应该在字段的规定范围内
2.2. 修改数据
sql
update 表名 set 字段名1=值1,字段名2=值2,...[where 条件];
注意:修改语句的条件可以有,也可以没有,如果没有条件,就会修改整张表的数据
2.3. 删除数据
sql
delete from 表名 [where 条件];
注意:
- DELETE 语句的条件可以有,也可以没有,如果没有条件,就会删除整张表的数据
- DELETE 不能删除某一个字段的值(可以使用 UPDATE)
3: DQL
3.1. 基本查询
sql
# 查询多个字段
select 字段1,字段2,字段3 from 表名;
select * from 表名;
# 设置别名
select 字段1 [as 别名1],字段2 [as 别名2],... from 表名;
# 去除重复记录
select distinct 字段列表 from 表名;
3.2. 条件查询
-
语法
sqlselect 字段列表 from 表名 where 条件;
-
条件
- 比较运算符:=,>,<,>=,<=,<>,!=,BETWEEN...AND...,IN,LIKE,IS NULL,IS NOT NULL
- 逻辑运算符:and,&&,or,||,not,!
3.3. 聚合查询
将一列数据作为一个整体,进行纵向计算
-
常见聚合函数
- count():统计个数
- sum():求和
- avg():求平均值
- max():求最大值
- min():求最小值
-
语法
sqlselect 聚合函数(字段名) from 表名;
注意:null 值不参与所有聚合函数运算
3.3. 分组查询
-
语法
sqlselect 字段列表 from 表名 [where 条件] group by 字段名 [having 分组后过滤条件];
-
where 与 having 的区别
- 执行时机不同:where 是分组之前进行过滤,不满足 where 条件的数据,不参与分组;having 是分组之后对结果进行过滤
- 判断条件不同:where 不能对聚合函数进行判读,而 having 可以
注意:
- 执行顺序: where -> 聚合函数 -> having
- 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义
3.4. 排序查询
-
语法
sqlselect 字段列表 from 表名 order by 字段名1 [asc|desc],字段名2 [asc|desc];
-
排序方式
- asc:升序(降序)
- desc:降序
注意:如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序
3.5. 分页查询
-
语法
sqlselect 字段列表 from 表名 limit 开始位置,获取数量;
注意:
- 起始索引从 0 开始,起始索引 = (查询页码 - 1) * 每页显示条数
- 分页查询是数据库的方言,不同的数据库有不同的表现,MySQL 是 LIMIT
- 如果查询是第一页数据,起始索引可以省略,直接简写为 limit 10
3.6. 顺序
-
编写顺序
sqlselect 字段列表 from 表名 where 条件 group by 分组字段列表 having 分组后条件列表 order by 排序字段列表 limit 分页参数
-
执行顺序
sqlfrom 表名 where 条件 group by 分组字段列表 having 分组后条件列表 select 字段列表 order by 排序字段列表 limit 分页参数
3.7. 案例
sql
# 查询年龄为20,21,22,23岁的员工信息
select * from emp where gender = 0 and age in (20,21,22,23);
# 查询性别为男,并且年龄在20-40岁(含)以内的姓名为三个字的员工
select * from emp where gender = 1 and (age between 20 and 40) and name like '___';
# 统计员工表中,年龄小于60岁的,男性员工和女性员工的人数
select gender,count(*) from emp where age < 60 group by gender;
# 查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序
select name,age from emp where age <= 35 order by age, hire_date desc ;
# 查询性别为男,且年龄在20-40岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序
select * from emp where gender = 1 and age between 20 and 40 order by age, hire_date limit 5;
4: DCL
4.1. 管理用户
-
查询用户
sqluse mysql; select * from user;
-
创建用户
sqlcreate user '用户名'@'主机名' identified by '密码';
-
修改用户密码
sqlalter user '用户名'@'主机名' identified with caching_sha2_password by '新密码';
-
删除用户
sqldrop user '用户名'@'主机名';
注意:
- 主机名可以使用 % 通配
- 这类 SQL 开发人员操作比较少,主要是 DBA 使用
4.2. 权限控制
- all, all privileges:所有权限
- select:查询权限
- insert:插入权限
- update:更新权限
- delete:删除权限
- create:创建权限
- drop:删除权限
- alter:修改权限
sql
# 查询权限
show grants for '用户名'@'主机名';
# 授予权限
grant 权限 on 数据库名.表名 to '用户名'@'主机名';
# 撤销权限
revoke 权限 on 数据库名.表名 from '用户名'@'主机名';
注意:
- 多个权限之间,使用逗号分隔
- 授权时,数据库名和表名可以使用 * 进行通配,代表所有
二:函数
2.1. 字符串函数
sql
select 函数(参数);
- concat(s1,s2,...):拼接字符串
- lower(str):将字符串转换为小写
- upper(str):将字符串转换为大写
- lpad(str,len,padstr):左填充,用字符串 pad 对 str 的左边进行填充,达到 len 个字符串长度
- rpad(str,len,padstr):右填充,用字符串 pad 对 str 的右边进行填充,达到 len 个字符串长度
- trim(str):去除字符串两端的空格
- substr(str,start,len):截取字符串,从 start 位置开始,截取 len 个字符
2.2. 数值函数
- ceil(x):向上取整
- floor(x):向下取整
- mod(x,y):返回 x / y 的模
- rand():返回 0-1 内的随机数
- round(x,d):四舍五入,保留 y 位小数
2.3. 日期函数
- curdate():返回当前日期
- curtime():返回当前时间
- now():返回当前日期和时间
- year(date):返回日期的年份
- month(date):返回日期的月份
- day(date):返回日期的天数
- date_add(date,interval expr ltype):返回一个日期/时间值加上一个时间间隔 expr 的时间值
- datediff(date1,date2):返回两个日期之间相差的天数
2.4. 流程函数
- if(value,t,f):如果 value 为真,则返回 t,否则返回 f
- ifnull(value1,value2):如果 value1 不为空,则返回 value1,否则返回 value2
- case when [val1] then [res1] ... else [default] end:如果 val1 为 true,则返回 res1,否则返回 default
- case [expr] when [val1] then [res1] ... else [default] end:如果 expr = val1,则返回 res1,否则返回 default
三:约束
3.1. 概述
-
概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据
-
目的:保证数据库中数据的正确,有效性和完整性
-
分类
- 非空约束:限制该字段的数据不能为 null => not null
- 唯一约束:保证该字段的所有数据都是唯一、不重复的 => unique
- 主键约束:主键是一行数据的唯一标识,要求非空且唯一 => primary key
- 默认约束:保存数据时,如果未指定该字段的值,则采用默认值 => default
- 检查约束:保证字段值满足某一个条件 => check
- 外键约束:用来让两张表的数据之间建立连接,保证数据的一致性和完整性 => foreign key
注意:约束是作用于表中字段上的,可以在创建表/修改表时添加约束
3.2. 外键约束
外键约束用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性
-
添加外键
sqlCREATE TABLE 表名( 字段名 字段类型, ... [CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名) ); ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名); -- 例子 alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);
-
删除外键
sqlALTER TABLE 表名 DROP FOREIGN KEY 外键名;
-
删除/更新行为
- no action:当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新(与 RESTRICT 一致)
- restrict:同 no action
- cascade:当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则也删除/更新外键在子表中的记录
- set null:当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为 null(要求该外键允许为 null)
- set default:父表有变更时,子表将外键设为一个默认值(Innodb 不支持)
sqlALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) on UPDATE 行为 on DELETE 行为;
四:多表查询
4.1. 多表关系
- 一对多(多对一)
- 多对多
- 一对一
-
一对多
案例:部门与员工
关系:一个部门对应多个员工,一个员工对应一个部门
实现:在多的一方建立外键,指向一的一方的主键
-
多对多
案例:学生与课程
关系:一个学生可以选多门课程,一门课程也可以供多个学生选修
实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
-
一对一
案例:用户与用户详情
关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率
实现:在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
4.2. 多表查询
4.2.1. 笛卡尔积
两个集合 A 集合和 B 集合的所有组合情况(在多表查询时,需要消除无效的笛卡尔积)
sql
# 笛卡尔积
select * from employee, dept;
# 消除无效笛卡尔积:
select * from employee, dept where employee.dept = dept.id;
4.2.2. 内连接
内连接查询的是两张表交集的部分
sql
# 隐式内连接:
select 字段列表 from 表1, 表2 where 条件 ...;
# 显式内连接:
select 字段列表 from 表1 [ inner ] join 表2 on 连接条件 ...;
显式性能比隐式高
sql
-- 查询员工姓名,及关联的部门的名称
-- 隐式
select e.name, d.name from employee as e, dept as d where e.dept = d.id;
-- 显式
select e.name, d.name from employee as e inner join dept as d on e.dept = d.id;
4.2.3. 外连接
-
左外连接:
查询左表所有数据,以及两张表交集部分数据
sqlselect 字段列表 from 表 1 LEFT [ outer ] join 表 2 on 条件 ...;
相当于查询表 1 的所有数据,包含表 1 和表 2 交集部分数据
-
右外连接:
查询右表所有数据,以及两张表交集部分数据
sqlselect 字段列表 from 表 1 RIGHT [ outer ] join 表 2 on 条件 ...;
sql
-- 左
select e.*, d.name from employee as e left outer join dept as d on e.dept = d.id;
select d.name, e.* from dept d left outer join emp e on e.dept = d.id; -- 这条语句与下面的语句效果一样
-- 右
select d.name, e.* from employee as e right outer join dept as d on e.dept = d.id;
4.2.4. 自连接
当前表与自身的连接查询,自连接必须使用表别名
sql
select 字段列表 from 表A 别名A join 表A 别名B on 条件 ...;
自连接查询,可以是内连接查询,也可以是外连接查询
sql
-- 查询员工及其所属领导的名字
select a.name, b.name from employee a, employee b where a.manager = b.id;
-- 没有领导的也查询出来
select a.name, b.name from employee a left join employee b on a.manager = b.id;
4.2.5. 联合查询 union, union all
把多次查询的结果合并,形成一个新的查询集
sql
select 字段列表 from 表A ...
union [all]
select 字段列表 from 表B ...
注意:
- 对于联合查询的多张表列数必须保持一致,字段类型也需要保持一致
- union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重
4.2.6. 子查询
SQL 语句中嵌套 select 语句,称谓嵌套查询,又称子查询
sql
select * from t1 where column1 = ( select column1 from t2);
子查询外部的语句可以是 INSERT / UPDATE / DELETE / select 的任何一个
-
根据子查询结果可以分为:
- 标量子查询(子查询结果为单个值)
- 列子查询(子查询结果为一列)
- 行子查询(子查询结果为一行)
- 表子查询(子查询结果为多行多列)
-
根据子查询位置可分为:
- WHERE 之后
- FROM 之后
- select 之后
4.2.6.1. 标量子查询
子查询返回的结果是单个值(数字、字符串、日期等)。
常用操作符:- < > > >= < <=
sql
-- 查询销售部所有员工
select id from dept where name = '销售部';
-- 根据销售部部门ID,查询员工信息
select * from employee where dept = 4;
-- 合并(子查询)
select * from employee where dept = (select id from dept where name = '销售部');
-- 查询xxx入职之后的员工信息
select * from employee where entrydate > (select entrydate from employee where name = 'xxx');
4.2.6.2. 列子查询
返回的结果是一列(可以是多行)。
常用操作符:
- in:在指定的集合范围内,多选一
- any / some:子查询返回列表中,有任意一个满足即可
- not in:不在指定的集合范围内
- all:子查询返回列表中,全部满足才行
sql
-- 查询销售部和市场部的所有员工信息
select * from employee where dept in (select id from dept where name = '销售部' or name = '市场部');
-- 查询比财务部所有人工资都高的员工信息
select * from employee where salary > all(select salary from employee where dept = (select id from dept where name = '财务部'));
-- 查询比研发部任意一人工资高的员工信息
select * from employee where salary > any (select salary from employee where dept = (select id from dept where name = '研发部'));
4.2.6.3. 行子查询
返回的结果是一行(可以是多列)。
常用操作符:=, <, >, IN, NOT IN
sql
-- 查询与xxx的薪资及直属领导相同的员工信息
select * from employee where (salary, manager) = (12500, 1);
select * from employee where (salary, manager) = (select salary, manager from employee where name = 'xxx');
4.2.6.4. 表子查询
返回的结果是多行多列
常用操作符:IN
sql
-- 查询与xxx1,xxx2的职位和薪资相同的员工
select * from employee where (job, salary) in (select job, salary from employee where name = 'xxx1' or name = 'xxx2');
-- 查询入职日期是2006-01-01之后的员工,及其部门信息
select e.*, d.* from (select * from employee where entrydate > '2006-01-01') as e left join dept as d on e.dept = d.id;
4.3. 多表查询案例
五:事务
事务是一组操作的集合,事务会把所有操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
5.1. 基本操作:
sql
-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额-1000
update account set money = money - 1000 where name = '张三';
-- 此语句出错后张三钱减少但是李四钱没有增加
模拟sql语句错误
-- 3. 将李四账户余额+1000
update account set money = money + 1000 where name = '李四';
5.1.1. 方式一
-
查看事务提交方式
1 为自动提交,0 为手动提交,该设置只对当前会话有效
sqlselect @@autocommit; set @@autocommit = 0;
-
提交事务
sqlcommit;
-
回滚事务
sqlrollback;
设置手动提交后上面代码改为:
sql
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;
5.1.2. 方式二
-
开启事务
sqlstart transaction 或 begin transaction;
-
提交事务:
sqlcommit;
-
回滚事物
sqlrollback;
sql
start transaction;
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;
5.2. 事物四大特性
- 原子性(Atomicity):事务是不可分割的最小操作但愿,要么全部成功,要么全部失败
- 一致性(Consistency):事务完成时,必须使所有数据都保持一致状态
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
5.3. 并发事物问题
- 脏读:一个事务读到另一个事务还没提交的数据
- 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同
- 幻读:一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在
5.4. 事物隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable Read (默认) | × | × | √ |
Serializable | × | × | × |
注意:
- √ 表示在当前隔离级别下该问题会出现
- Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差
sql
# 查看事务隔离级别:
select @@transaction_isolation;
# 设置事务隔离级别:
set [ session | global ] transaction isolation level {read uncommitted | read committed | repeatable read | serializable };
session 是会话级别,表示只针对当前会话有效,global 表示对所有会话有效