mysql 基础
一、 概述:
1、课程内容和数据相关概念
数据库的概念:存储数据的仓库,数据是又组织的进行存储
数据库管理系统:操作和管理数据库的大型软件
SQL: 操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准
2、关系型数据库:
建立在关系模型基础上,由多张表相关连接的二维表组成的数据库
数据模型:
客户端 --> DBMS --> 数据库1、数据库2 --> 表1、表2
二、SQL
1、SQL通用语法:关键字建议大写
2、SQL分类:DDL(Data Definition Language)、DML(Data Manipulation Language)、DQL(Data Query Language)、DCL(Data Control Language).
3、DDL: 数据库定义语言,创建数据库、创建表
a、查询数据库:
语法: SHOW DATABASES; —> 查询所有数据库
语法: SELECT DATABASE(); —>查询当前数据库
b、创建数据库
语法: CREATE DATABASE [ IF NOT EXISTS ] 数据库名 [ DEFAULT CHARSET 字符集(UTF-8)] [ COLLATE 排序规则 ];
c、删除数据库
语法: DROP DATABASE [ IF EXISTS ] 数据库名;
d、使用数据库
语法: USE 数据库名;
e、表操作-> 创建&查询表
使用数据库后,查看表 :SHOW TABLES;
查询表结构 :DESC 表名;
查询指标的创建语句 : SHOW CREATE TABLE 表名;
创建表结构:
语法:CREATE TABLE 表名(
字段1 字段1类型 [ COMMENT 字段1 注释 ],
字段2 字段2类型 [ COMMENT 字段2 注释 ],
……
字段n 字段n类型 [ COMMENT 字段n 注释 ]
) [ COMMENT 表注释 ];
f、 表操作 --> 数据类型:mysql 数据类型分为很多,主流的是数值型、字符串类型、日 期时间类型
01: MySQL 数值数据类型
02:MySQL 字符串数据类型
char 和 vachar 相比 char的性能比vachar 的性能更好。
03: MySQL 日期型数据类型
04 : 案例:
DROP TABLE IF EXISTS tb_emp;
CREATE TABLE tb_emp(
id int COMMENT '编号',
workno VARCHAR(10) COMMENT '员工编号',
name VARCHAR(10) COMMENT '员工姓名',
sex CHAR(1) COMMENT '性别',
age TINYINT UNSIGNED COMMENT '年龄',
identy CHAR(18) COMMENT '身份证',
entry DATE COMMENT '入职时间'
)COMMENT '员工表';
g 表操作–>修改
01 添加字段:ALTER TABLE 表名 ADD 字段名 字段类型[(长度)] [COMMENT ‘备注‘] [约束];
02 修改字段:
修改字段类型:
语法:ALTER TABLE 表名 MODIFY 字段名 新数据类型 [(长度)] ;
修改字段名和字段类型:
语法:ALTER TABLE 表名 CHANGE 字段名 新字段名 数据类型[(长度)] [COMMENT ‘备注’] [约束];
案例 : 将tb_emp 的nickname修改为username 数据类型为 varchar(30)
ALTER TABLE tb_emp CHANGE nickname username varchar(30);
03 删除表字段
语法:ALTER TABLE 表名 DROP 字段名;
案例 :删除 tb_emp的username 字段;
语法:ALTER TABLE tb_emp DROP username;
04 修改表名
ALTER TABLE 表名 RENAME TO 新表名;
案例: 修改tb_emp 的表名为 tb_employee
语法: ALTER TABLE tb_emp RENAME TO tb_employee;
05 表操作 -->删除表: 删除表时,表的数据也会被删除。
删除指定表 不安全模式,删除表后 无法恢复 慎用
语法: DROP TABLE [ IF EXISTS ] 表名;
删除指定表,并重新创建一个具有相同结构的空表
语法: TRUNCATE TABLE 表名;
案例: 删除tb_user 表 DROP TABLE IF EXISTS tb_user;
案例: 删除 tb_employee 表 并保留其表结构
语法: TRUNCATE TABLE tb_employee;
4、DML: 数据库操作语言,用于对数据表中的数据进行增删改操作
a、INSERT 插入(增加)数据(黑马 P12课程 需要重新观看一次)
给指定字段插入数据:
语法: INSERT INTO 表名 (字段名1,字段名2,…) VALUES (value1,value2,…);
备注: 字段名1和value1 对应 字段名2 和value2 对应,…
给表的全部字段插入数据
语法: INSERT INTO 表名 VALUES (值1,值2,…);
备注: 此时必须将值的数据全部和字段进行对应
批量插入:
语法: INSERT INTO 表名 (字段名1,字段名2,…) VALUES (value1,value2,…),(value1,value2,…),(value1,value2,…),…;
语法: INSERT INTO 表名 VALUES (值1,值2,…),(值1,值2,…),(值1,值2,…),…;
注意:插入数据时,字段的顺序要和给定的值的顺序一一对应,字符串和日期应该包含在引号中,给定的数值不能超过字段定义的大小
b、UPDATE 修改数据
语法: UPDATE 表名 SET 字段1=值1,字段2=值2,… [WHERE 条件…];
注意: 修改语句的条件可以有,也可以没有,如果没有条件,那么会修改整个表的数据
案例:
# 案例 将所有员工的入职日期修改为“2021-01-18”
update tb_employee SET entry='2021-01-18';
c、DELETE 删除数据
DELETE 语句的条件可以有,也可以没有,如果没有条件,那么会删除整张表的数据 DELETE 语句不能删除某一个字段的值(可以使用update 语句)
语法: DELETE FROM 表名 WHERE 条件
5、DQL: 数据库查询语言,用来查询数据库中的表记录
查询关键字 SELECT
完整格式
语法: SELECT * [ 字段1 [ AS 别名 ],字段2,… ] FROM 表名1 [ AS 别名 ],[ 表名2,… ] [ WHERE 条件1 AND 条件2 AND… ] [ GROUP BY 分组字段1,分组字段2,… ] [ HAVING 分组后条件列表 ] [ ORDER BY 排序字段列表 ] [ LIMIT 分页参数 ]
如图:
a、基本查询:
01: 查询多个字段 SELECT 字段列表 FROM 表名
语法: SELECT * FROM 表名;
语法: SELECT 字段1 [AS 别名1],字段2 [AS 别名2],… FROM 表名;
去除重复记录: DISTINCT 关键字 , 语法:SELECT DISTINCT 字段列表 FROM 表名;
案例: 见下方脚本块
DROP TABLE IF EXISTS employee;
# 创建表 employee
CREATE TABLE employee
(
id int COMMENT '编号',
workno VARCHAR(10) COMMENT '员工编号',
name VARCHAR(10) COMMENT '员工姓名',
gender CHAR(1) COMMENT '性别',
age TINYINT UNSIGNED COMMENT '年龄',
idcard CHAR(18) COMMENT '身份证',
workaddress varchar(50) COMMENT '工作地址',
entry DATE COMMENT '入职时间'
) COMMENT '员工表';
# 插入 数据
INSERT INTO employee(id, workno, name, gender, age, idcard, workaddress, entry)
values (1, '1', '柳岩', '女', 20, '123456789012345670', '北京', '2000-01-01'),
(2, '2', '张无忌', '男', 18, '123456789012345671', '北京', '2005-09-01'),
(3, '3', '韦一笑', '男', 38, '123456789012345672', '上海', '2005-08-01'),
(4, '4', '赵敏', '女', 18, '123456789012345673', '北京', '2009-12-01'),
(5, '5', '小昭', '女', 16, '123456789012345674', '上海', '2007-07-01'),
(6, '6', '杨逍', '男', 28, '123456789012345675', '北京', '2006-01-01'),
(7, '7', '范瑶', '男', 40, '123456789012345676', '北京', '2005-05-01'),
(8, '8', '黛绮丝', '女', 38, '123456789012345677', '天津', '2015-05-01'),
(9, '9', '范凉凉', '女', 45, '123456789012345678', '北京', '2010-04-01'),
(10, '10', '陈丕亮', '男', 53, '123456789012345679', '上海', '2011-01-01'),
(11, '11', '张士诚', '男', 55, '12345678901234567a', '江苏', '2015-05-01'),
(12, '12', '常遇春', '男', 32, '12345678901234567b', '北京', '2004-02-01'),
(13, '13', '张三丰', '男', 88, '12345678901234567c', '江苏', '2020-11-01'),
(14, '14', '灭绝', '女', 65, '12345678901234567d', '北京', '2019-05-01'),
(15, '15', '胡青牛', '男', 70, '12345678901234567x', '西安', '2018-04-01'),
(16, '16', '周芷若', '女', 18, null, '西安', '2012-06-01');
# 查询所有字段
SELECT * FROM employee;
SELECT id,name,workno,idcard,workaddress,gender,age,entry from employee;
# 查询指定字段
SELECT id, name, gender FROM employee;
# 查询所有员工的工作地址 起别名
SELECT workaddress as '工作地址' FROM employee;
# 查询工作地址,不重复
SELECT distinct workaddress FROM employee;
02: 条件查询
语法: SELECT 字段列表 FROM 表名 WHERE 条件列表;
案例: 见 下方代码块
-- 条件查询
# 1 查询年龄等于18的员工
SELECT * FROM employee WHERE age = 18;
# 2 查询年龄小于20的员工
SELECT * FROM employee WHERE age<20;
# 3 查询年龄小于等于28的员工
SELECT * FROM employee WHERE age <=28;
# 4 查询没有身份证号码的员工
SELECT * FROM employee WHERE idcard is null ;
# 5 查询有身份证号码的员工
SELECT * FROM employee WHERE idcard is not null;
# 6 查询年龄不等于88 的员工信息
SELECT * FROM employee WHERE age!= 88;
# 7 查询年龄在15岁到28岁的员工
SELECT * FROM employee WHERE age between 15 and 28;
# 8 查询性别为女的员工
SELECT * FROM employee WHERE gender = '女';
# 9 查询年龄等于18或者20 或者40 的员工
SELECT * FROM employee WHERE age in (18,20,40);
# 10 查询姓名为两个字的员工信息
SELECT * FROM employee WHERE name like '__';
# 11 查询身份证号最后一位是X的员工
SELECT * FROM employee WHERE idcard like '%X';
03:聚合函数
语法: SELECT 聚合函数( 字段名) FROM 表名;
案例: 见下
-- 聚合函数
# 1 统计该表中的员工数量
SELECT COUNT(id) FROM employee;
# 2 统计该表中的员工平均年龄
SELECT name,AVG(age) FROM employee;
# 3 统计员工的最大年龄
SELECT name,MAX(age) FROM employee;
# 4 统计员工的最小年龄
SELECT name,MIN(age) FROM employee;
# 5 统计员工的年龄之和
SELECT SUM(age) FROM employee;
04: 分组查询
语法:SELECT 字段列表 FROM 表名 [WHERE 条件列表] GROUP BY 分组字段名[HAVING 分组后过滤条件];
注意: WHERE 和 HAVING 的区别
1: 执行的时机不同,WHERE是在分组之前执行,不满足WHERE条件的不参与分组;而HAVING是分组之后对查询出的结果进行过滤。
2: 判断的时机不同,WHERE 不能对聚合函数进行判断,而HAVING 能
*** 执行顺序: WHERE > 聚合函数 > having
*** 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段没有任何意义。
案例:
-- 分组查询
# 1 根据性别分组,统计男性员工和女性员工的数量
SELECT COUNT(gender) as '男性人数',(SELECT count(gender) FROM employee WHERE gender='女') as '女性人数' FROM employee WHERE gender='男';
SELECT sum(IF(gender='男',1,0)) as '男性人数',
sum(IF(gender='女',1,0)) as '女性人数'
FROM employee;
# 2 根据性别分组,统计男性员工和女性员工的平均年龄
SELECT gender '性别', AVG(age) '平均年龄' FROM employee GROUP BY gender;
SELECT AVG(IF(gender='男',age,null)) AS '男性平均年龄', AVG(IF(gender='女',age,null)) AS '女性平均年龄' FROM employee;
# 3 查询年龄小于45的员工,并根据工作地址分组,获取员工数量大于等于3的工作地址
SELECT workaddress,count(id) emp_sum FROM employee WHERE age<45 GROUP BY workaddress HAVING emp_sum >=3;
05: 排序查询
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1,字段2 排序方式2;
排序方式:
ASC 升序 (默认值)
DESC 降序
案例:
-- 排序查询
# 1 根据年龄对员工进行升序排序
SELECT * FROM employee ORDER BY age ;
# 2 根据入职时间对员工降序排序
SELECT * FROM employee ORDER BY entry DESC ;
# 3 根据年龄升序排序,年龄相同按照入职年龄降序排序
SELECT * FROM employee ORDER BY age asc ,entry DESC ;
06: 分页查询
SELECT 字段列表 FROM 表名 LIMIT 起始索引,查询记录数;
注意:
起始索引从0 开始,起始索引=(查询页码-1)* 每页记录数据
分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中的是LIMIT
如果查询的是第一页的数据,起始索引可以省略,直接简写为LIMIT10
案例:
-- 分页查询
# 1 查询第一页的员工数据,每页展示10条记录
SELECT * FROM employee LIMIT 10;
# 2 查询第二页的员工数据,每页展示10条记录 (2-1)*10 = 起始索引
SELECT * FROM employee LIMIT 10,10;
练习 :
# 1 查询年龄为20,21,22,23的员工信息
SELECT * FROM employee WHERE age in (20,21,22,23);
SELECT * FROM employee WHERE age BETWEEN 20 AND 23;
# 2 查询性别为男,并且年龄在20-40(含)以内的姓名为三个字的员工
SELECT * FROM employee WHERE gender ='男' AND age >=20 and age <=40 and name like '___';
SELECT * FROM employee WHERE gender ='男' AND age BETWEEN 20 and 40 and name like '___';
# 3 统计员工表中,年龄小于60岁,男性员工和女性员工的人数
SELECT SUM(IF(gender='男',1,0)) AS '男性人数', SUM(IF(gender='女',1,0)) AS '女性人数' FROM employee WHERE age < 60
# 4 查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序
SELECT name,age,entry FROM employee WHERE age <=35 ORDER BY age,entry DESC;
# 5 查询性别为男,且年龄在20-40岁(含)以内的前五个员工信息,并对查询结果按年龄升序排序,年龄相同按入职时间升序排序
SELECT * FROM (SELECT * FROM employee WHERE gender ='男' AND age >20 and age <=40 LIMIT 5) res ORDER BY res.age,res.entry;
总结:
SQL的编写顺序
SELECT 字段列表 FROM 表名列表 WHERE 条件列表 GROUP BY 分组字段列表 HAVING 分组后条件列表 ORDER BY 排序字段列表 LIMIT 分页参数
SQL 执行顺序
FROM 表名列表 WHERE 条件列表 GROUP BY 分组字段列表 HAVING 分组后条件列表 SELECT 字段列表 ORDER BY 排序字段列表 LIMIT 分页参数
6、DCL: 数据库控制语言,用来创建数据库用户、控制数据库的访问、权限
a 、管理用户
01: 查询用户
Use mysql;
SELECT * FROM user;
02: 创建用户
CREATE USER ‘用户名‘@‘主机名‘ IDENTIFIED BY ‘密码‘;
03: 修改用户密码
ALTER USER ‘用户名‘@’主机名’ IDENTIFIED WITH mysql_native_password BY ‘新密码‘;
04: 删除用户
DROP USER ‘用户名‘@’主机名’ ;
注意: 主机名可以通过 % 来通配
案例:
-- 管理用户
# 查询用户
Use mysql;
SELECT * FROm user;
# 创建用户
# 1. 创建用户 itcast 只能在当前主机localhost访问,密码为123456
CREATE USER 'itcast'@'localhost' IDENTIFIED BY '123456';
# 2. 创建用户 heima 可以在任意主机访问该数据库,密码为123456
CREATE USER 'heima'@'%' IDENTIFIED BY '123456';
# 修改用户
# 修改用户heima 的密码为 1234
ALTER USER 'heima'@'%' IDENTIFIED WITH mysql_native_password by '1234';
# 删除用户
# 删除itcast@localhost 用户
DROP USER 'heima'@'%';
DROP USER 'itcast'@'localhost';
b: 权限控制
01:查询权限
SHOW GRANTS FOR ‘用户名‘@’主机名’;
02:授予权限
GRANT 权限列表 ON 数据库名.表名 TO ‘用户名‘@’主机名’;
03:撤销权限
REVOKE 权限列表 ON 数据库名.表名 FROM ‘用户名‘@’主机名’;
注意:
多个权限之间通过逗号分隔。
授权和回收权限时,数据库名和表名可以通过 * 来进行通配,代表所有
总结:
三、函数
1、字符串函数
练习:
-- ---------------------函数演示----------------------------
-- concat 将两个字符串拼接
SELECT concat('hello',' mysql');
-- lower 转换成小写
SELECT lower('HELLO');
-- upper 转换成大写
SELECT upper('hello');
-- LPAD(a,b,c),用字符c在字符a的左边补全到b位
select LPAD('0',3,'01');
SELECT LPAD('1',4,'0');
-- RPAD(a,b,c) 用字符c在字符a的右边补全到b位
SELECT RPAD('1',5,'20');
-- TRIM 去除目标字符串的首尾空格
SELECT TRIM(' HELLO, WORLD ');
-- SUBSTRING(str,start,len) 将字符串从指定位置起(包含,下标以1开头)截取指定长度,并返回
SELECT SUBSTRING('HELLO,WORLD',3,3);
# 1, 将企业的工号统一改为五位 如 1号员工的工号为00001 10号员工的工号为00010
update employee set workno=LPAD(workno,5,'0');
2、数值函数
练习:
# 2 数值函数
-- CEIL(X) 对x进行向上取整
SELECT CEIL(21.2); # 22
-- FLOOR(x) 对x进行向下取整
SELECT FLOOR(22.1); # 22
-- MOD(x,y) 返回x/y的模(余数)
SELECT MOD(22,3); # 1
-- rand() 返回0-1的随机数
SELECT RAND();
-- round(x,y),求x的四舍五入的值,保留y位小数
SELECT ROUND(2.2132100212,8); #2.21321002
# 通过数据库的函数,生成一个六位数的随机验证码
SELECT LPAD(ROUND(rand()*1000000,0),6,0);
3、日期函数
练习:
-- ------------------------日期函数--------------------
# curdate() 返回当前日期
SELECT CURDATE();
# curtime() 返回当前时间
SELECT CURTIME();
# now() 返回当前时间和日期
SELECT NOW();
# YEAR(date) 获取指定date的年份
SELECT YEAR('2021-04-21');
# MONTH(date) 获取指定date的月份
SELECT MONTH('2022-11-20');
# DAY(date) 获取指定date的日期
SELECT DAY('2021-11-22');
# DATE_ADD(date,INTERVAL expr type) 返回日期/时间值加上一个时间间隔expr后的时间值
SELECT DATE_ADD('2022-11-21',INTERVAL 210 year );
# DATEDIFF(DATE1,DATE2) 返回起始时间DATE1 和结束时间DATE2之间的天数,一般较大的日期放在第一个参数,较小的日期放在第二个参数上
SELECT DATEDIFF('2022-11-22','2022-04-21');
# 查询所有员工的入职天数,并根据入职天数倒序排序
SELECT * FROM (SELECT DATEDIFF(CURDATE(),entry) as day_from_now FROM employee) res ORDER BY res.day_from_now desc;
SELECT DATEDIFF(CURDATE(),entry) AS '入职天数' FROM employee ORDER BY entry asc ;
4、流程函数
练习:
-- ------------------------流程函数--------------------
# IF(condition,t,f) 如果满足condition 条件,那么值为t ,否则为f
SELECT IF(true,'ok','error');
SELECT IF(false,'ok','ERROR');
# IFNULL(VALUE1,VALUE2) 如果VALUE1不为空,返回VALUE1 ,否则返回VALUE2
SELECT IFNULL('ok','default');
SELECT IFNULL('','default');
SELECT IFNULL(null,'default');
# CASE WHEN [val1] THEN[res1] ... ELSE[DEFAULT] END 如果val1为true,返回res1,否则返回default默认值
SELECT CASE WHEN 1=2 THEN 'ok' else 'default' end;
# 查询员工表中的姓名和工作地址(北京/上海----> 一线城市, 其他的二线城市)
SELECT name , (CASE WHEN workaddress in('北京','上海') THEN '一线城市' ELSE '二线城市' END) AS '工作地址' FROM employee ;
SELECT name, (case workaddress when '上海' then '一线城市' when '北京' then '一线城市' else '二线城市' end) as '工作地址' FROM employee;
# case[expr] WHEN [val1] THEN [res1]... ELSE[DEFAULT] END 如果expr的值等于val1, 返回res1,... 否则返回default 默认值
SELECT case 1 WHEN 2 THEN 'ok' ELSE 'default' END;
总结:
四、约束
01:表内约束
概念:约束是作用于表中字段上的规则,用户限制存储在表中的数据
目的: 保证数据库中数据的正确性、有效性和完整性。
分类:
约束 | 描述 | 关键字 |
---|---|---|
非空约束 | 限制该字段的数据不能为null | NOT NULL |
唯一约束 | 保证该字段的所有数据都是唯一、不重复的 | UNIQUE |
主键约束 | 主键是一行数据的唯一标识,要求非空且唯一 | PRIMARY KEY |
默认约束 | 保存数据时,如果未指定该字段的值,则采用默认值 | DEFAULT |
检查约束(8.0.16 版本之后) | 保证字段值满足某一个条件 | CHECK |
外键约束 | 用来让两张表的数据之间建立连接,保证数据的一致性和完整性 | FOREIGN KEY |
注意: 约束是作用域表字段上的,可以通过创建表和修改表的时候添加约束。
约束演示:
外键约束:
概念:外键用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性
创建员工和部门表
DROP DATABASE IF EXISTS itheima;
CREATE DATABASE itheima;
use itheima;
DROP TABLE IF EXISTS dept;
CREATE TABLE dept(
id int auto_increment primary key comment '主键,id唯一标识符',
deptName varchar(50) not null comment '部门名称'
) comment '部门表';
INSERT INTO dept (id, deptName) VALUES (1,'研发部'), (2,'市场部'), (3,'财务部'), (4,'销售部'), (5,'总经办');
DROP TABLE IF EXISTS emp;
CREATE TABLE emp(
id int auto_increment primary key comment'编号,唯一标识符',
name varchar(50) not null comment '姓名',
age int comment '年龄',
job varchar(50) comment '职位',
salary int comment '薪资',
entrydate date comment '入职时间',
managerid int comment '直系领导ID',
dept_id int comment '部门ID'
) COMMENT '员工表';
# alter table emp add age int comment '年龄';
INSERT INTO emp(id, name,age,job, salary, entrydate, managerid, dept_id) VALUES
(1,'金庸',66,'总裁',20000,'2000-01-01',null,5),
(2,'张无忌',20,'项目经理',15200,'2005-01-01',1,1),
(3,'杨潇',33,'开发',8400,'2011-01-01',2,1),
(4,'韦一笑',48,'开发',11000,'2006-01-01',2,1),
(5,'常遇春',43,'开发',10500,'2002-01-01',2,1),
(6,'小昭',19,'开发实习生',3200,'2018-01-01',2,1);
部门表和员工表在在数据库层面没有创建外键管理,不能保证数据的一致性和完整性。
添加外键的语法:
CREATE TABLE 表名(
字段名 数据类型
.....
[CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名称) REFERENCES 主表(主表列名)
);
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表(主表列名);
案例:
-- 添加外键
ALTER TABLE emp ADD CONSTRAINT fk_emp_deptId_dept_id FOREIGN KEY (dept_id) REFERENCES dept(id);
-- 删除外键
ALTER TABLE emp drop foreign key fk_emp_deptId_dept_id;
外键约束:
删除/更新行为:
语法:
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段) ON UPDATE CASCADE ON DELETE CASCADE;
案例:
-- 添加外键
ALTER TABLE emp ADD CONSTRAINT fk_emp_deptId_dept_id FOREIGN KEY (dept_id) REFERENCES dept(id);
-- 删除外键
ALTER TABLE emp drop foreign key fk_emp_deptId_dept_id;
ALTER TABLE emp add constraint fk_emp_deptId_dept_id foreign key(dept_id) references dept(id) ON DELETE cascade on UPDATE cascade;
总结:
五、多表查询
01:多表关系
在实际项目开发过程中,会根据业务需求及业务模块之间的关系,分析并设计表结构,基于业务之间相互关联,所以各个表之间也存在相互关联关系,具体分为三种,如:
一对一:
案例: 用户与用户详情的关系
关系: 一对一关系,多用于单表拆分,将一张表的基础子弹放在一张表中其他详情字段放在另一张表中,用以提升效率。
一对多(多对一):
案例: 一个部门对应多个员工,一个员工对应一个部门。
实现方式:在多的一方创建外键,指向一的一方。
示例:
多对多:
案例: 学生和课表的关系 ,一个学生可以对应多门课,一个课程对应有多个学生学习
实现方式:建立第三张表作为中间表,中间表至少包含两个外键,分别外键关联学生表和课程表
示例:
-- ------------------------多表查询 多对多关系示例----------------------
# 创建学生表
DROP TABLE IF EXISTS student;
CREATE TABLE student(
id int auto_increment primary key comment 'id 主键 唯一标识符',
name varchar(50) comment '姓名',
no varchar(10) comment '学号'
) COMMENT '学生表';
# 插入数据
insert into student values (null,'黛绮丝','2000100101'),(null,'谢逊','2000100102'),(null,'殷天正','2000100103'),(null,'韦一笑','2000100104');
# 创建课程表
DROP TABLE IF EXISTS course;
CREATE table course(
id int auto_increment primary key comment 'id 主键 唯一标识符',
name varchar(10) comment '课程名称'
)comment '课程表';
# 插入数据
insert into course values (null,'java'),(null,'php'),(null,'MySQL'),(null,'hadoop');
# 创建中间表
DROP TABLE IF EXISTS student_course;
CREATE TABLE student_course(
id int auto_increment primary key comment 'id 主键 唯一标识符',
studentid int not null comment '学生id',
courseid int not null comment '课程id',
constraint fk_courseid foreign key(courseid) references course(id),
constraint fk_studentid foreign key (studentid) references student(id)
)comment '学生课程中间表';
insert into student_course values (null,1,1),(null,1,2),(null,1,3),(null,2,2),(null,2,3),(null,3,4);
图示:
02:多表查询概述: 指从多张表中查询数据
多表查询的笛卡尔积现象: 笛卡尔积是指在数据学中,两个集合的所有组合情况,(在多表查询时,我们需要消除无效的笛卡尔积)。
03:内连接 —> 相当于查询A集合和B集合的部分数据
隐式内联:
语法:SELECT 字段列表 FROM 表1,表2 WHERE 条件…;
显式内联:
语法:SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件…;
实例:
-- --------内联演示-----------
# 查询每一个员工的姓名,及关联部门的名称(使用隐式内联实现)
SELECT e.name , d.deptName from emp e,dept d where e.dept_id = d.id;
# 查询每一个员工的姓名,及关联部门的名称(使用显示内联实现)
SELECT e.name , d.deptName from emp e inner join dept d on e.dept_id = d.id;
04:外连接
左外连接: 查询坐标所有数据,以及两张表交集部分数据
右外连接: 查询右表所有数据,以及两张表交集部分数据
演示:
# 查询emp表的所有数据,和对应的部门信息(左外连接)
SELECT * FROM emp e left join dept d on d.id = e.dept_id;
# 查询dept表的所有数据,和对应的员工信息(右外连接)
SELECT * FROM emp e right join dept d on d.id = e.dept_id;
05:
a:自查询 —> 当前表与自身的连接查询,自连接必须使用别名
自连接语法:
SELECT 字段列表 FROM 表A,别名A JOIN 表A 别名B ON 条件…
自连接查询可以是外连接查询,也可以是内连接查询。
示例:
# 查询员工及其领导的名字
SELECT a.name as '员工姓名',b.name as '领导姓名' FROM emp a join emp b on a.managerid = b.id;
# 查询所有员工及其领导的名字 ,如果员工没有领导也要查询出来
SELECT a.name as '员工姓名',b.name as '领导姓名' FROM emp a left join emp b on a.managerid= b.id;
b:联合查询 --> union ,union all
对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集
语法: SELECT 字段列表 FROM 表A …
UNION [ ALL ]
SELECT 字段列表 FROM 表B…;
注意:对于联合查询的多张表的列 数据必须保持一致,字段类型也需要保持一致。
实例:
-- 连接查询 使用union 和union all
# 将薪资低于10000和年龄大于50的所有员工查询出来
SELECT * FROM emp where salary< 10000
union all
SELECT * FROM emp WHERE age >50;
07:子查询
概念: SQL语句中嵌套SELECT 语句,称为嵌套查询,又称为子查询
语法: SELECT * FROM tmp WHERE column in (SELECT column from tmp2);
子查询的外层可以是任意的 INSERT / UPDATE / DELETE / SELECT 语句中的一个。
根据子查询的查询结果分为:
标量子查询: 子查询结果为单个值(数字、字符串、日期等),这种是最简单的形式。
常见的操作符为:= <> > < <= >=
列子查询: 子查询的结果为一列(可以是多行)
常见的操作符: IN 、NOT IN 、ANY 、SOME、 ALL
IN :在指定的集合范围之内,多选一
NOT IN :不在指定的集合范围内
ANY :子查询返回列表中,有任意一个满足即可
SOME : 与ANY等同,使用SOME的地方都可以使用ANY
ALL :子查询返回列表的所有值都必须满足
示例:
-- --------列子查询--------
# 1. 查询'销售部'和'市场部'的所有员工信息
SELECT * FROM emp WHERE dept_id in (select id from dept WHERE deptName in ('市场部','销售部'));
# 2. 查询比财务部所有人工资都高的员工信息
SELECT * FROM emp WHERE salary > (select max(salary) From emp group by dept_id having dept_id = (select id from dept where deptName= '财务部'));
SELECT * FROM emp WHERE salary > all (select salary from emp where dept_id = (select id from dept where deptName='财务部'));
# 3. 查询比研发部其中任意一人工资高的员工信息
SELECT * FROM emp WHERE salary > any (select salary FROM emp WHERE dept_id = (SELECT id from dept where deptName='研发部'));
行子查询: 子查询的结果为一行(也可以是多行),这种子查询称为行子查询
常用操作符: = 、<> 、IN 、NOT IN
注意:条件中的多个字段顺序应当和子查询的字段顺序一致,否则会没有数据
# 查询和张无忌工资相同、直属领导相同的的员工信息
SELECT * FROM emp where (managerid,salary) = (SELECT salary,managerid from emp where name ='张无忌');
表子查询: 子查询结果为多行多列
常用操作符:IN
示例:
# 查询 杨潇 张无忌 工资相同、直系领导相同的员工信息
SELECT * FROM emp where (salary,managerid) in (select salary,managerid from emp where name in ('张无忌','杨潇'));
# 查询入职日期是'2006-01-01'之后的日期,及其部门信息。
SELECT * FROM (select * from emp where entrydate >'2006-01-01') e left join dept d on e.dept_id = d.id;
08:多表查询案例
六、事务
1: 事务简介:是一组操作的集合,他是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求,即这些操作要么同时成功,要么同时失败。
具体例子 参见 转账业务。
MySQL 默认是事务自动提交,也就是说,当执行一条DML SQL语句,MySQL 会隐式的提交事务
2: 事务操作
– 方式一:设置手动事务
01:查看 / 设置 事务的提交方式
SELECT @@autocommit;
# 设置手动提交
SET @@autocommit=0;
02:提交事务
COMMIT;
03:回滚事务
ROLLBACK;
– 方式二:开启事务模式
01:开启事务
START TRANSACTION 或 BEGIN;
02:提交事务
COMMIT;
03:回滚事务
ROLLBACK;
3:事务四大特性
a、原子行(ATOMICITY): 事务是不可分割的最小操作单元,那么全部成功,要么全部失败
b、一致性(CONSISITENCY):事务完成时,必须是所有的数据都保持一致状态。
c、隔离性(ISOLATION): 数据库系统提供的隔离机制,保证事务在不收外部并发操作影响 的独立环境下运行
d、持久性(DURABILITY):事务一旦提交或者回滚,他对数据库的修改是永久的。
4:并发事务问题
问题 | 描述 |
---|---|
脏读 | 一个事务读到另外一个事务还没有提交的数据 |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。 |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现数据已经存在,好像出现了“幻影” |
5:事务的隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | √ | √ | √ |
READ COMMITTED | × | √ | √ |
REPEATABLE READ(默认) | × | × | √ |
SERIALIZABLE | × | × | × |
查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE } ;
事务的隔离级别也高,数据越安全,但是性能越低。
事务总结:
希望对您有帮助。求点赞+关注。