创建一个员工表包含如下列(id, name, age, dep_name, dep_location),id主键并自动增长,添加5条数据
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
dep_name VARCHAR(30),
dep_location VARCHAR(30)
);
-- 添加数据
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('张三', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('李四', 21, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('王五', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('老王', 20, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('大王', 22, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('小王', 18, '销售部', '深圳');
缺点:表中出现了很多重复的数据(数据冗余),如果要修改研发部的地址需要修改3个地方。
-- 创建部门表
CREATE TABLE department (
id INT PRIMARY KEY AUTO_INCREMENT,
dep_name VARCHAR(20),
dep_location VARCHAR(20)
);
-- 创建员工表
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
dep_id INT
);
-- 添加2个部门
INSERT INTO department (dep_name, dep_location) VALUES ('研发部', '广州');
INSERT INTO department (dep_name, dep_location) VALUES('销售部', '深圳');
-- 添加员工,dep_id表示员工所在的部门
INSERT INTO employee (NAME, age, dep_id) VALUES
('张三', 20, 1),
('李四', 21, 1),
('王五', 20, 1),
('老王', 20, 2),
('大王', 22, 2),
('小王', 18, 2);
问题:
当我们在employee的dep_id里面输入不存在的部门,数据依然可以添加.但是并没有对应的部门,不能出现这种情况。employee的dep_id中的内容只能是department表中存在的id
目标:需要约束dep_id字段的值, 只能是department表中已经存在id
解决方式:使用外键约束
有些情况下,使用一张表表示数据 数据不好维护, 存在数据冗余,比较乱的现象
使用多张表,需要对数据进行约束,不约束添加的数据也会不合法
表和表之间存在一种关系,但是这个关系需要谁来维护和约束?
保证引用完整性. 用来维护多表间关系
外键: 一张从表中的某个字段引用主表中的主键
主表: 约束别人
副表/从表: 使用别人的数据,被别人约束
1. 新建表时增加外键:
[CONSTRAINT] [外键约束名称] FOREIGN KEY(外键字段名) REFERENCES 主表名(主键字段名)
关键字解释:
CONSTRAINT -- 约束关键字
FOREIGN KEY(外键字段名) –- 某个字段作为外键
REFERENCES -- 主表名(主键字段名) 表示参照主表中的某个字段
2. 已有表增加外键:
ALTER TABLE 从表 ADD [CONSTRAINT] [外键约束名称] FOREIGN KEY (外键字段名) REFERENCES 主表(主键字段名);
-- 删除员工表,从新创建
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
dep_id INT,
-- 添加一个外键
-- 外键取名公司要求,一般fk结尾
CONSTRAINT emp_depid_ref_dep_id_fk FOREIGN KEY(dep_id) REFERENCES department(id)
);
-- 添加正确数据
INSERT INTO employee (NAME, age, dep_id) VALUES
('张三', 20, 1),
('李四', 21, 1),
('王五', 20, 1),
('老王', 20, 2),
('大王', 22, 2),
('小王', 18, 2);
-- 验证: 添加错误数据
INSERT INTO employee (NAME, age, dep_id) VALUES ('二王', 20, 5);// 报错
ALTER TABLE 表 drop foreign key 外键名称;
-- 删除
ALTER TABLE employee DROP FOREIGN KEY emp_depid_ref_dep_id_fk;
-- 从新为已经存在的表增加外键
ALTER TABLE employee ADD CONSTRAINT emp_depid_ref_dep_id_fk FOREIGN KEY(dep_id) REFERENCES department(id);
要把部门表中的id值2,改成5,能不能直接修改呢?
UPDATE department SET id=5 WHERE id=2;
不能直接修改:Cannot delete or update a parent row: a foreign key constraint fails 如果副表(员工表)中有引用的数据,不能直接修改主表(部门表)主键
要删除部门id等于1的部门, 能不能直接删除呢?
DELETE FROM department WHERE id = 1;
不能直接删除:Cannot delete or update a parent row: a foreign key constraint fails 如果副表(员工表)中有引用的数据,不能直接删除主表(部门表)数据
什么是级联操作:
在修改和删除主表的主键时,同时更新或删除副表的外键值,称为级联操作
ON UPDATE CASCADE
– 级联更新,主键发生更新时,外键也会更新
ON DELETE CASCADE
– 级联删除,主键发生删除时,外键也会删除
具体操作:
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
dep_id INT,
CONSTRAINT employee_dep_fk FOREIGN KEY (dep_id) REFERENCES department(id) ON UPDATE CASCADE ON DELETE CASCADE
);
INSERT INTO employee (NAME, age, dep_id) VALUES ('张三', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('李四', 21, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('王五', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('老王', 20, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('大王', 22, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('小王', 18, 2);
UPDATE department SET id=10 WHERE id=1;
DELETE FROM department WHERE id=2;
alter table 表 add foreign key(列[外键]) references 表(列[主键]) [ON UPDATE CASCADE][ON DELETE CASCADE]
eg: 下订单(t_order)—>谁下(t_user), 买了什么(t_product)
现实生活中,实体与实体之间肯定是有关系的,比如:老公和老婆,部门和员工,老师和学生等。那么我们在设计表的时候,就应该体现出表与表之间的这种关系!分成三种:
例如:班级和学生,部门和员工,客户和订单
一的一方: 班级 部门 客户
多的一方:学生 员工 订单
一对多建表原则: 在从表(多方的一方)创建1一个字段,字段作为外键指向主表(一方)的主键
多对多(m:n)
例如:老师和学生,学生和课程,用户和角色
一个老师可以有多个学生,一个学生也可以有多个老师 多对多的关系
一个学生可以选多门课程,一门课程也可以由多个学生选择 多对多的关系
一个用户可以有多个角色,一个角色也可以有多个用户 多对多的关系
多对多关系建表原则: 需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键。
一对一(1:1)
例如: 一个公司可以有一个注册地址,一个注册地址只能对一个公司。
例如:一个老公可以有一个老婆,一个老婆只能由一个老公
在实际的开发中应用不多.因为一对一可以创建成一张表。
两种建表原则:
需求:完成一个学校的选课系统,在选课系统中包含班级,学生和课程这些实体。
分析:
班级和学生: 一对多
学生和课程: 多对多
班级和学生之间是有关系存在:
一个班级下包含多个学生,一个学生只能属于某一个班级(一对多的关系)。
学生和课程之间是有关系存在:
一个学生可以选择多门课程,一门课程也可以被多个学生所选择(多对多的关系)。
-- 班级表
create table class(
cid int primary key auto_increment,
cname varchar(30)
);
-- 学生表
create table student(
sid int primary key auto_increment,
sname varchar(40),
age int,
cno int,
constraint stu_c_fk1 foreign key(cno) references class(cid)
);
-- 课程表
create table course(
coid int primary key auto_increment,
coname varchar(40)
);
-- 创建中间表
create table stu_cou(
sno int,
cono int,
constraint stu_cou_fk2 foreign key(sno) references student(sid),
constraint stu_cou_fk3 foreign key(cono) references course(coid)
);
-- 创建部门表
CREATE TABLE dept (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
-- 创建员工表
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
gender CHAR(1), -- 性别
salary DOUBLE, -- 工资
join_date DATE, -- 入职日期
dept_id INT
);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);
交叉查询把若干张表(>=2)没有条件的连接在一起,进行展示
select a.列,a.列,b.列,b.列 from a,b ;
select a.*,b.* from a,b ;
--或者
select * from a,b;
SELECT * FROM dept, emp;
以上数据其实是左表的每条数据和右表的每条数据组合。左表有3条,右表有5条,最终组合后3*5=15条数据。
左表的每条数据和右表的每条数据组合,这种效果称为笛卡尔乘积
交叉查询产生这样的结果并不是我们想要的,那么怎么去除错误的,不想要的记录呢,当然是通过条件过滤。通常要查询的多个表之间都存在关联关系,那么就通过==关联关系(主外键关系)==去除笛卡尔积。
隐式里面是没有inner关键字的
select [字段,字段,字段][*] from a,b where 连接条件 --(a表里面的主键 = b表里面的外键)
显示里面是有inner关键字的
select [字段,字段,字段][*] from a [inner] join b on 连接条件 [ where 其它条件]
-- 课堂sql
-- 隐式内连接
-- 语法: select [字段,字段,字段][*] from a,b where 连接条件 --(a表里面的主键 = b表里面的外键)
select * from dept,emp where dept.id = emp.dept_id; -- 内连接 显示所有信息
-- 内连接 显示部门名称,员工名称,工资,性别
select dept.name,emp.name,emp.salary,emp.gender from dept,emp where dept.id = emp.dept_id;
-- 简化: 区别名 工作中常见
select d.name,e.name,e.salary,e.gender from dept as d,emp as e where d.id = e.dept_id;
select d.name,e.name,e.salary,e.gender from dept d,emp e where d.id = e.dept_id; -- 掌握
-- 显示内连接查询
-- 语法: select [字段,字段,字段][*] from a [inner] join b on 连接条件 [ where 其它条件]
select * from dept inner join emp on dept.id = emp.dept_id;
select d.`NAME`,e.`NAME`,e.salary from dept d inner join emp e on d.id = e.dept_id;
select d.`NAME`,e.`NAME`,e.salary from dept d join emp e on d.id = e.dept_id;
查询所有部门下的员工信息,如果该部门下没有员工则不展示.
-- 2.1 隐式内连接方式
SELECT * FROM dept , emp where emp.`dept_id`=dept.`id`;
-- 2.2 显示内连接方式
SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id`;
内连接的特点(查的是什么东西)
内连接查询的是公共部分,满足连接条件(主外键关系)的部分
使用内连接的关键点
语法
-- 隐式(不出现inner)
select * from a,b where a.主键=b.外键 and 其它条件
-- 显示(出现inner)
select * from a inner join b on a.主键=b.外键 where 其它条件
我们发现内连接查询出来的是公共部分. 如果要保证某张表的全部数据情况下进行连接查询. 那么就要使用外连接查询了. 外连接分为左外连接和右外连接
以join左边的表为主表,展示主表的所有数据,根据条件查询连接右边表的数据,若满足条件则展示,若不满足则以null显示.
可以理解为:在内连接的基础上保证左边表的数据全部显示
select [字段][*] from a left [outer] join b on 条件
SSELECT * FROM dept LEFT OUTER JOIN emp ON emp.`dept_id`=dept.`id`;
以join右边的表为主表,展示右边表的所有数据,根据条件查询join左边表的数据,若满足则展示,若不满足则以null显示
可以理解为:在内连接的基础上保证右边表的数据全部显示
select 字段 from a right [outer] join b on 条件
SELECT * FROM dept RIGHT OUTER JOIN emp ON emp.dept_id=dept.id;
select * from a left outer join b on 连接条件 --左外连接
select * from a right outer join b on 连接条件 --右外连接
1.用户1和订单m
查询所有的用户的订单信息 外连接
查询下单的用户的信息 内连接
2.用户1和账户m
查询所有的用户的账户信息 外连接
查询所有用户的开户信息 内连接
我们刚刚讲解了内连接和外连接查询, 但是如果遇到很复杂的场景, 内连接和外连接查询可能查询不出来.我们就可以使用子查询了.
直观一点: 一个查询里面至少包含2个select
一个查询语句的结果作为另一个查询语句的条件
有查询的嵌套,内部的查询称为子查询
子查询要使用括号
子查询结果的三种情况:
尽管子查询的语法很灵活,没有固定的写法.但是它也有一些规律.
子查询结果只要是单个值
,肯定在WHERE
后面作为条件
SELECT 查询字段 FROM 表 WHERE 字段[= > < <>](子查询);
查询工资最高的员工是谁?
SELECT MAX(salary) FROM emp;
SELECT * FROM emp WHERE salary=(SELECT MAX(salary) FROM emp);
查询工资小于平均工资的员工有哪些?
SELECT AVG(salary) FROM emp;
SELECT * FROM emp WHERE salary < (SELECT AVG(salary) FROM emp);
子查询结果只要是单列
,肯定在WHERE
后面作为条件
子查询结果是单列多行,结果集类似于一个数组,父查询使用IN
运算符
SELECT 查询字段 FROM 表 WHERE 字段 IN (子查询);
查询工资大于5000的员工,来自于哪些部门的名字
SELECT dept_id FROM emp WHERE salary > 5000;
SELECT dept.name FROM dept WHERE dept.id IN (SELECT dept_id FROM emp WHERE salary > 5000);
查询开发部与财务部所有的员工信息
SELECT id FROM dept WHERE NAME IN('开发部','财务部');
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME IN('开发部','财务部'));
子查询结果只要是多行多列
,肯定在FROM
后面作为表
SELECT 查询字段 FROM (子查询) 表别名 WHERE 条件;
子查询作为表需要取别名,否则这张表没用名称无法访问表中的字段
查询出2011年以后入职的员工信息,包括部门名称
SELECT * FROM emp WHERE join_date > '2011-1-1';
SELECT * FROM dept d, (SELECT * FROM emp WHERE join_date > '2011-1-1') e WHERE e.dept_id = d.id;
select ... from ... where 列 = (子查询)
select ... from ... where 列 in (子查询)
select ... from (子查询) as 别名
-- 账户表
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
insert into account values (null,'zs',1000);
insert into account values (null,'ls',1000);
insert into account values (null,'ww',1000);
保证一组操作全部成功或者失败。
一条sql语句就是一个事务
方式一: 手动开启事务的方式 【掌握】
start transaction;开启事务
commit;提交
rollback;回滚
方式二: 设置MYSQL中的自动提交的参数【了解】
查看MYSQL中事务是否自动提交
show variables like '%commit%';
设置自动提交的参数为OFF
set autocommit = 0;-- 0:OFF 1:ON
在某些成功的操作完成之后,后续的操作有可能成功有可能失败,但是不管成功还是失败,前面操作都已经成功,可以在当前成功的位置设置一个回滚点。可以供后续失败操作返回到该位置,而不是返回所有操作,这个点称之为回滚点。
将数据还原到1000
开启事务
让张三账号减3次钱
设置回滚点:savepoint three_times;
让张三账号减4次钱
回到回滚点:rollback to three_times;
插入大量的数据的时候. 1亿条数据 需要插入很久. 要求: 1亿条数据是一个整体,要么全部插入成功的 要么都不插入成功.
手动开启事务 语法
start transaction; -- 开启事务
commit; -- 提交
rollback; -- 回滚
注意
原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
eg: zs 1000; ls 1000;
zs 给 ls转100
要么都发生zs 900; ls 1100;
要么都不发生zs 1000; ls 1000;
一致性(Consistency)事务前后数据的完整性必须保持一致.
eg: zs 1000; ls 1000; 一共2000
zs 给 ls转100
要么都发生zs 900; ls 1100; 一共2000
要么都不发生zs 1000; ls 1000; 一共2000
持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
eg: zs 1000 给小红 转520, 张三 提交了
隔离性(Isolation)事务的隔离性是指多个用户并发操作数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。 简单来说: 事务之间互不干扰
事务在操作时的理想状态: 所有的事务之间保持隔离,互不影响。因为并发操作,多个用户同时访问同一个数据。可能引发并发访问的问题
可以通过设置事物隔离级别解决读的问题
级别 | 名字 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 数据库默认隔离级别 |
---|---|---|---|---|---|---|
1 | 读未提交 | read uncommitted | 是 | 是 | 是 | |
2 | 读已提交 | read committed | 否 | 是 | 是 | Oracle |
3 | 可重复读 | repeatable read | 否 | 否 | 是 | MySQL |
4 | 串行化 | serializable | 否 | 否 | 否 |
隔离级别越高,性能(效率)越差,安全性越高。
set session transaction isolation level 隔离级别;
eg: 设置事务隔离级别为read uncommitted,read committed,repeatable read,serializable
set session transaction isolation level read uncommitted;
select @@tx_isolation;
在服务器进行数据传输、数据存储和数据交换,就有可能产生数据故障。比如发生意外停机或存储介质损坏。这时,如果没有采取数据备份和数据恢复手段与措施,就会导致数据的丢失,造成的损失是无法弥补与估量的。
mysqldump -u用户名 -p密码 数据库 > 文件的路径
SOURCE 导入文件的路径;
注意:还原的时候需要先登录MySQL,并选中对应的数据库
好的数据库设计对数据的存储性能和后期的程序开发,都会产生重要的影响。
建立科学的,规范的数据库就需要满足一些规则来优化数据的设计和存储,这些规则就称为范式。
数据库表的每一列都是不可分割的原子数据项,不能是集合、数组等非原子数据项。即表中的某个列有多个值时,必须拆分为不同的列。简而言之,第一范式每一列不可再拆分,称为原子性
如果不遵守第一范式,查询出数据还需要进一步处理(查询不方便)。遵守第一范式,需要什么字段的数据就查询什么数据(方便查询)
**在满足第一范式的前提下,表中的每一个字段都完全依赖于主键。**所谓完全依赖是指不能存在仅依赖主键一部分的列。简而言之,第二范式就是在第一范式的基础上所有列完全依赖于主键列。当存在一个复合主键包含多个主键列的时候,才会发生不符合第二范式的情况。比如有一个主键有两个列,不能存在这样的属性,它只依赖于其中一个列,这就是不符合第二范式。
简而言之,第二范式需要满足:
如果不准守第二范式,数据冗余,相同数据无法区分。遵守第二范式减少数据冗余,通过主键区分相同数据。
在满足第二范式的前提下,表中的每一列都直接依赖于主键,而不是通过其它的列来间接依赖于主键。简而言之,第三范式就是所有列不依赖于其它非主键列,也就是在满足2NF的基础上,任何非主列不得传递依赖于主键。所谓传递依赖,指的是如果存在"A → B → C"的决定关系,则C传递依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系:主键列 → 非主键列x → 非主键列y
如果不准守第三范式,可能会有相同数据无法区分,修改数据的时候多张表都需要修改(不方便修改)。遵守第三范式通过id可以区分相同数据,修改数据的时候只需要修改一张表(方便修改)。
- 能够理解外键约束
==保证数据引用完整性\正确性.== 用来维护多表间关系
外键: 一张从表中的某个字段引用主表中的主键
主表: 约束别人
副表/从表: 使用别人的数据,被别人约束
- 能够说出多表之间的关系及其建表原则
1. 1对多: 在多方创建一个列作为外键 指向一方的主键
2. 多对多: 创建一张中间表,这个表里面至少包含两个列,都作为外键,指向一方的主键
3. 1对1: 先当做1对多, 再在外键列添加唯一约束
- 能够使用内连接进行多表查询
显示: select [字段,字段,字段][*] from a [inner] join b on 连接条件 [ where 其它条件]
隐式: select [字段,字段,字段][*] from a, b where 连接条件
- 能够使用左外连接和右外连接进行多表查询
左外连接 select [字段][*] from a left [outer] join b on 条件
右外连接 select [字段][*] from a right [outer] join b on 条件
- 能够使用子查询进行多表查询
子查询结果一个值:select ... from 表 where 条件 [= < > <>] (子查询)
子查询结果单列多个值:select ... from 表 where 条件 in (子查询)
子查询结果多列多行: select ... from 表 ,(子查询) as 别名 where 条件
子查询结果多列多行: select ... from 表 left\right join (子查询) as 别名 on 条件
- 能够理解事务的概念
事务: 要么全部成功,要么全部失败
- 能够在MySQL中使用事务
开启事务: start transaction;
提交事务:commit;
回滚事务: rollback;
回滚点: savepoint 名; rollback to 回滚点名;-- 没有结束事务
- 能够完成数据的备份和恢复
navicat----
- 能够理解三大范式
略