文章目录
- 外键约束
- 表关联关系
- E-R模型图
- 表关联查询
外键约束
- 约束 : 约束是一种限制,它通过对表的行或列的数据做出限制,来确保表的数据的完整性、关联性
- foreign key 功能 : 建立表与表之间的某种约束的关系,由于这种关系的存在,能够让表与表之间的数据,更加的完整,关连性更强,为了具体说明创建如下部门表和人员表。
- 示例
-- 创建部门表
CREATE TABLE dept (id int PRIMARY KEY auto_increment,dname VARCHAR(50) not null);
insert into dept values
(1,"技术部"),
(2,"销售部"),
(3,"市场部"),
(4,"行政部"),
(5,'财务部'),
(6,'总裁办公室');
-- 创建人员表
CREATE TABLE person (
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(32) NOT NULL,
age tinyint unsigned,
salary decimal(8,2),
dept_id int
) ;
insert into person values
(1,"Lily",29,20000,2),
(2,"Tom",27,16000,1),
(3,"Joy",30,28000,1),
(4,"Emma",24,8000,4),
(5,"Abby",28,17000,3),
(6,"Jame",32,22000,3);
上面两个表中每个人员都应该有指定的部门,但是实际上在没有约束的情况下人员是可以没有部门的或者也可以添加一个不存在的部门,这显然是不合理的。
-
主表和从表:若同一个数据库中,B表的外键与A表的主键相对应,则A表为主表,B表为从表。
-
foreign key 外键的定义语法:
[CONSTRAINT symbol] FOREIGN KEY(外键字段) REFERENCES tbl_name (主表主键) [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}] [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
该语法可以在 CREATE TABLE 和 ALTER TABLE 时使用
-- 创建表时直接简历外键 CREATE TABLE person ( id int PRIMARY KEY AUTO_INCREMENT, name varchar(32) NOT NULL, age tinyint unsigned, salary decimal(10,2), dept_id int , constraint dept_fk foreign key(dept_id) references dept(id) );
建立表后增加外键
alter table person add
constraint dept_fk
foreign key(dept_id)
references dept(id);
注意:
- 并不是任何情况表关系都需要建立外键来约束,如果没有类似上面的约束关系时也可以不建立。
- 从表的外键字段数据类型与指定的主表主键应该相同。
-
通过外键名称解除外键约束
alter table person drop foreign key dept_fk; -- 查看外键名称 show create table person;
注意:删除外键后发现desc查看索引标志还在,其实外键也是一种索引,需要将外键名称的索引删除之后才可以。
-
级联动作
-
restrict(默认) : on delete restrict on update restrict
- 当主表删除记录时,如果从表中有相关联记录则不允许主表删除
- 当主表更改主键字段值时,如果从表有相关记录则不允许更改
-
cascade :数据级联更新 on delete cascade on update cascade
-
当主表删除记录或更改被参照字段的值时,从表会级联更新
alter table person add constraint dept_fk foreign key(dept_id) references dept(id) on delete cascade on update cascade;
-
-
set null : on delete set null on update set null
-
当主表删除记录时,从表外键字段值变为null
-
当主表更改主键字段值时,从表外键字段值变为null
alter table person add constraint dept_fk foreign key(dept_id) references dept(id) on delete set null on update set null;
-
-
表关联关系
当我们应对复杂的数据关系的时候,数据表的设计就显得尤为重要,认识数据之间的依赖关系是更加合理创建数据表关联性的前提。一对多和多对多是常见的表数据关系:
- 一对多关系
一张表中有一条记录可以对应另外一张表中的多条记录;但是反过来,另外一张表的一条记录
只能对应第一张表的一条记录,这种关系就是一对多或多对一举例: 一个人可以拥有多辆汽车,每辆车登记的车主只有一人。
create table person(
id varchar(32) primary key,
name varchar(30),
age int
);
create table car(
id varchar(32) primary key,
brand varchar(30),
price decimal(10,2),
pid varchar(32),
foreign key(pid) references person(id)
);
- 多对多关系
一对表中(A)的一条记录能够对应另外一张表(B)中的多条记录;同时B表中的一条记录
也能对应A表中的多条记录举例:一个运动员可以报多个项目,每个项目也会有多个运动员参加,这时为了表达多对多关系需要单独创建关系表。
CREATE TABLE athlete (
id int primary key AUTO_INCREMENT,
name varchar(30),
age tinyint NOT NULL,
country varchar(30) NOT NULL
);
CREATE TABLE item (
id int primary key AUTO_INCREMENT,
rname varchar(30) NOT NULL
);
CREATE TABLE athlete_item (
id int primary key auto_increment,
aid int NOT NULL,
tid int NOT NULL,
FOREIGN KEY (aid) REFERENCES athlete (id),
FOREIGN KEY (tid) REFERENCES item (id)
);
-- 关系表中添加排名字段
alter table athlete_item add ranking int after tid;
表关系设计:
用户朋友圈表内容表:
姓名 密码 电话 图片 内容 地点 时间 点赞 评论
create table user(
id int primary key auto_increment,
name varchar(30),
passwd char(64),
tel char(16)
);
create table friends(
id int primary key auto_increment,
image char(50),
content varchar(1024),
time datetime ,
address varchar(50),
user_id int ,
foreign key(user_id) references user(id)
);
create table like_comment(
id int primary key auto_increment,
`like` bit,
comment text,
user_id int ,
friends_id int ,
foreign key(user_id) references user(id),
foreign key(friends_id) references friends(id)
);
E-R模型图
- 定义
E-R模型(Entry-Relationship)即 实体-关系 数据模型,用于数据库设计
用简单的图(E-R图)反映了现实世界中存在的事物或数据以及他们之间的关系
- 实体、属性、关系
实体
1、描述客观事物的概念
2、表示方法 :矩形框
3、示例 :一个人、一本书、一杯咖啡、一个学生
属性
1、实体具有的某种特性
2、表示方法 :椭圆形
3、示例
学生属性 :学号、姓名、年龄、性别、专业 ...
感受属性 :悲伤、喜悦、刺激、愤怒 ...
关系
1、实体之间的联系
2、一对多关联(1:n)
3、多对多关联(m:n)
- E-R图的绘制
矩形框代表实体,菱形框代表关系,椭圆形代表属性
表关系设计:
将book表拆分为三张表(图书 作家 出版社),自行设计表之间的关系,通过E-R图分析表关系和属性,然后写出创建语句
create table author(
id int primary key auto_increment,
name varchar(20),
sex char,
age tinyint
);
create table press(
id int primary key auto_increment,
pname varchar(20),
tel char(16),
address varchar(512)
);
create table book(
id char(18) primary key,
bname varchar(30),
price float,
author_id int,
press_id int ,
foreign key (author_id) references author(id),
foreign key (press_id) references press(id)
);
create table author_press(
id int primary key,
author_id int,
press_id int ,
foreign key (author_id) references author(id),
foreign key (press_id) references press(id)
);
表关联查询
如果多个表存在一定关联关系,可以多表在一起进行查询操作,其实表的关联整理与外键约束之间并没有必然联系,但是基于外键约束设计的具有关联性的表往往会更多使用关联查询查找数据。
- 简单多表查询
多个表数据可以联合查询,语法格式如下:
select 字段1,字段2... from 表1,表2... [where 条件]
e.g.
select c.name,c.score,h.hobby from class as c,hobby as h
where c.name=h.name;
select name,salary,dname from person,dept
where person.dept_id = dept.id;
select name,salary,dname from person,dept
where person.dept_id = dept.id and salary>=20000;
笛卡尔积现象就是将A表的每一条记录与B表的每一条记录强行拼在一起。所以,如果A表有n条记录,B表有m条记录,笛卡尔积产生的结果就会产生n*m条记录。
select * from class,hobby;
- 内连接
内连接查询只会查找到符合条件的记录,其实结果和表关联查询是一样的,官方更推荐使用内连接查询。
SELECT 字段列表
FROM 表1 INNER JOIN 表2
ON 表1.字段 = 表2.字段;
select name,salary,dname from person inner join dept
on person.dept_id = dept.id
where salary>=20000;
- 左连接 : 左表全部显示,显示右表中与左表匹配的项
SELECT 字段列表
FROM 表1 LEFT JOIN 表2
ON 表1.字段 = 表2.字段;
e.g.
select name,salary,dname
from person left join dept
on person.dept_id = dept.id
where salary>=20000;
-- 查询每个部门员工人数
select dname,count(name) from dept left join person on dept.id=person.dept_id group by dname;
- 右连接 :右表全部显示,显示左表中与右表匹配的项
SELECT 字段列表
FROM 表1 RIGHT JOIN 表2
ON 表1.字段 = 表2.字段;
e.g.
select dname,count(name)
from person right join dept
on person.dept_id = dept.id
group by dname;
注意:我们尽量使用数据量大的表作为基准表,放在前面。
create table class(cid int primary key auto_increment,caption char(4) not null);
create table teacher(tid int primary key auto_increment,tname varchar(32) not null);
create table student(sid int primary key auto_increment,
sname varchar(32) not null,
gender enum('male','female','others') not null default 'male',
class_id int,
foreign key(class_id) references class(cid)
on update cascade on delete cascade);
create table course(cid int primary key auto_increment,
cname varchar(16) not null,
teacher_id int,
foreign key(teacher_id) references teacher(tid)
on update cascade on delete cascade);
create table score(sid int primary key auto_increment,
student_id int,
course_id int,
number int(3) not null,
foreign key(student_id) references student(sid)
on update cascade on delete cascade,
foreign key(course_id) references course(cid)
on update cascade on delete cascade);
insert into class(caption) values('三年一班'),('三年二班'),('三年三班');
insert into teacher(tname) values('魏老师'),('祁老师'),('小泽老师');
insert into student(sname,gender,class_id) values('钢蛋','female',1),('铁锤','female',1),('山炮','male',2),('彪哥','male',3),('虎子','male',3),('妞妞','female',2),('建国','male',2);
insert into course(cname,teacher_id) values('生物',1),('体育',1),('物理',2);
insert into score(student_id,course_id,number) values(1,1,60),(1,2,59),(2,2,100),(3,2,78),(4,3,66),(2,3,78),(5,2,77),(6,1,84),(7,1,79),(5,3,80),(3,1,59);
1. 查询每位老师教授的课程数量
2. 查询各科成绩最高和最低的分数,形式 : 课程ID 课程名称 最高分 最低分
3. 查询平均成绩大于85分的所有学生学号,姓名和平均成绩
4. 查询课程编号为2且课程成绩在80以上的学生学号和姓名
5. 查询各个课程及相应的选修人数
6. 查询每位学生的姓名,所在班级和各科平均成绩
1. 查询每位老师教授的课程数量
select tname,count(cname)
from teacher left join course
on teacher.tid = course.teacher_id
group by tname;
2. 查询各科成绩最高和最低的分数,形式 : 课程ID 课程名称 最高分 最低分
select cid as 课程ID,cname as 课程名称,
max(number) as 最高分,min(number) as 最低分
from course left join score
on course.cid = score.course_id
group by cid,cname;
3. 查询平均成绩大于85分的所有学生学号,姓名和平均成绩
select student.sid,sname,avg(number)
from student left join score
on student.sid = score.student_id
group by student.sid,sname
having avg(number) > 85;
4. 查询课程编号为2且课程成绩在80以上的学生学号和姓名
select student.sid,sname,number
from student left join score
on student.sid = score.student_id
where course_id=2 and number>80;
5. 查询各个课程及相应的选修人数
select cname,count(course_id)
from course left join score
on course.cid = score.course_id
group by cname;
6. 查询每位学生的姓名,所在班级和平均成绩
select sname,caption,avg(number)
from student left join class
on student.class_id = class.cid
left join score
on student.sid = score.student_id
group by sname,caption;