Mysql多表、外键、和数据库设计

Mysql多表、外键、和数据库设计

多表

实际开发中,一个项目通常需要很多张表才能完成。

外键

添加外键约束

/*
	外键约束
		作用:外键约束可以让两表之间产生一个对应关系,从而保证主表数据完整性
	外键
		指的是在从表中与主表的主键对应的字段
	主表和从表
		主表 主键id所在表,一的一方
		从表 外键字段所在的表,多的一方
	添加外键格式
		1.创建表的时候添加外键
		create table 表名(
			字段...
			[constraint] [外键约束名] foreign key (外键字段名) references 主表(主键字段)
		);
*/

-- 创建部门表
-- 一方,主表
CREATE TABLE department(
id INT PRIMARY KEY AUTO_INCREMENT, 
dep_name VARCHAR(30),
dep_location VARCHAR(30)
);

-- 创建员工表,添加外键
CREATE TABLE employee(
	eid INT PRIMARY KEY AUTO_INCREMENT,
	ename VARCHAR(20),
	age INT,
	dept_id INT,	-- 外键字段 指向主表主键
	-- 添加外键约束
	CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id)
);

-- 添加2个部门
INSERT INTO department VALUES(NULL, '研发部','广州'),(NULL, '销售部', '深圳');
SELECT * FROM department;

-- 正常添加数据 (从表外键 对应主表主键)
INSERT INTO employee (ename, age, dept_id) VALUES ('张百万', 20, 1);
INSERT INTO employee (ename, age, dept_id) VALUES ('赵四', 21, 1);
INSERT INTO employee (ename, age, dept_id) VALUES ('广坤', 20, 1);
INSERT INTO employee (ename, age, dept_id) VALUES ('小斌', 20, 2);
INSERT INTO employee (ename, age, dept_id) VALUES ('艳秋', 22, 2);
INSERT INTO employee (ename, age, dept_id) VALUES ('大玲子', 18, 2);
SELECT * FROM employee;
-- 插入一条有问题的数据 (部门id不存在)
-- Cannot add or update a child row: a foreign key constraint fails
INSERT INTO employee (ename, age, dept_id) VALUES ('错误', 18, 3);

-- 创建表之后添加外键
-- 语法格式 ALTER TABLE 从表 ADD CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id)
ALTER TABLE employee ADD CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id);

外键删除

/*
	删除外键约束
		语法格式
			alter table 从表 drop foreign key 外键约束名称
*/

-- 删除employee表外键
ALTER TABLE employee DROP FOREIGN KEY emp_dept_fk;

/*
	外键约束注意事项
		1.主表的外键类型必须和主表类型保持一致
		2.添加数据时,应该先添加主表的数据
		3.删除数据时,需先删除从表数据
*/

级联删除

/*
	级联删除
		指的是在删除主表的数据同时,可以删除与之相关的从表中的数据
	语法格式
		on delete cascade
*/

-- 重新创建添加级联操作
CREATE TABLE employee(
eid INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20),
age INT,
dept_id INT,
CONSTRAINT emp_dept_fk FOREIGN KEY(dept_id) REFERENCES department(id)
-- 添加级联删除
ON DELETE CASCADE
);

多表关系设计

实际开发中,一个项目通常需要很多张表才能完成。例如:一个商城项目就需要分类表(category)、商品表(products)、订单表(orders)等多张表。且这些表的数据之间存在一定的关系,接下来我们一起学习一下多表关系设计方面的知识

表与表之间的三种关系

一对多关系: 最常见的关系, 学生对班级,员工对部门
多对多关系: 学生与课程, 用户与角色
一对一关系: 使用较少,因为一对一关系可以合成为一张表

一对多关系

例如:班级和学生,部门和员工,客户和订单,分类和商品

建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键

在这里插入图片描述

多对多关系

例如:老师和学生,学生和课程,用户和角色

建表原则:需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键。

在这里插入图片描述

一对一

在实际的开发中应用不多.因为一对一可以创建成一张表

建表原则:外键唯一 主表的主键和从表的外键(唯一),形成主外键关系,外键唯一 UNIQUE

设计 省&市表

省和市之间的关系是 一对多关系,一个省包含多个市

在这里插入图片描述

#创建省表 (主表,注意: 一定要添加主键约束)
CREATE TABLE province(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
description VARCHAR(20)
);
#创建市表 (从表,注意: 外键类型一定要与主表主键一致)
CREATE TABLE city(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
description VARCHAR(20),
pid INT,
-- 添加外键约束
CONSTRAINT pro_city_fk FOREIGN KEY (pid) REFERENCES province(id)
);

在这里插入图片描述

设计 演员与角色表

演员与角色 是多对多关系, 一个演员可以饰演多个角色, 一个角色同样可以被不同的演员扮演

在这里插入图片描述

#创建演员表
CREATE TABLE actor(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
#创建角色表
CREATE TABLE role(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
#创建中间表
CREATE TABLE actor_role(
-- 中间表自己的主键
id INT PRIMARY KEY AUTO_INCREMENT,
-- 指向actor 表的外键
aid INT,
-- 指向role 表的外键
rid INT
);
-- 为中间表的aid字段,添加外键约束 指向演员表的主键
ALTER TABLE actor_role ADD FOREIGN KEY(aid) REFERENCES actor(id);
-- 为中间表的rid字段, 添加外键约束 指向角色表的主键
ALTER TABLE actor_role ADD FOREIGN KEY(rid) REFERENCES role(id);

在这里插入图片描述

多表查询

数据准备

-- 创建 db3_2 数据库,指定编码
CREATE DATABASE db3_2 CHARACTER SET utf8;

创建分类表与商品表

#分类表 (一方 主表)
CREATE TABLE category (
cid VARCHAR(32) PRIMARY KEY ,
cname VARCHAR(50)
);
#商品表 (多方 从表)
CREATE TABLE products(
pid VARCHAR(32) PRIMARY KEY ,
pname VARCHAR(50),
price INT,
flag VARCHAR(2),   		#是否上架标记为:1表示上架、0表示下架   
category_id VARCHAR(32),
-- 添加外键约束
FOREIGN KEY (category_id) REFERENCES category (cid)
);
#分类数据
INSERT INTO category(cid,cname) VALUES('c001','家电');
INSERT INTO category(cid,cname) VALUES('c002','鞋服');
INSERT INTO category(cid,cname) VALUES('c003','化妆品');
INSERT INTO category(cid,cname) VALUES('c004','汽车');
#商品数据
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p001','小米电视机',5000,'1','c001');
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p002','格力空调',3000,'1','c001');
INSERT INTO products(pid, pname,price,flag,category_id) VALUES('p003','美的冰箱',4500,'1','c001');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p004','篮球鞋',800,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p005','运动裤',200,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p006','T恤',300,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p007','冲锋衣',2000,'1','c002');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p008','神仙水',800,'1','c003');
INSERT INTO products (pid, pname,price,flag,category_id) VALUES('p009','大宝',200,'1','c003');

笛卡尔积

在这里插入图片描述

/*
	多表查询语法
		select 字段名称 from 表名
*/

-- 笛卡儿积
-- 多表查询 交叉连接查询
SELECT * FROM products,category;

内连接

/*
	内连接查询
		特点 通过指定条件去匹配两张表中内容,匹配不上不显示
		
		隐式内连接
			语法格式 :select 字段名... from 左表,右表 where 连接条件
		显式内连接
			语法格式: select 字段名... from 左表 join 右表 on 连接条件
*/

-- 查询所有商品信息和对应的分类信息
SELECT * FROM products,category WHERE category_id = cid;

-- 查询商品表的商品名称 和 价格,以及商品的分类信息
SELECT p.pname,p.price,c.cname FROM products p,category c WHERE p.category_id = c.cid;

-- 查询 格力空调是属于哪一分类下的商品
SELECT p.pname,c.cname FROM products p,category c WHERE p.category_id = c.cid AND p.pname = '格力空调';
SELECT p.pname,c.cname FROM products p,category c WHERE p.category_id = c.cid AND p.pid = 'p002';

--  查询所有商品信息和对应的分类信息、
SELECT * FROM products p JOIN category c ON p.category_id = c.cid;

--  查询鞋服分类下,价格大于500的商品名称和价格
SELECT p.pname,p.price FROM products p JOIN category c ON p.category_id = c.cid AND p.price > 500 AND c.cname = '鞋服';

外连接

/*
	外连接
		左外连接
			语法格式 select 字段名 from 左表 left join 右表 on 连接条件 
			特点
				左表为基准匹配右表中的数据,若能够匹配则显现,若匹配不上左表正常显示,右表显示为null
		右外连接
			语法格式 select 字段名 from 左表 right join 右表 on 连接条件 
			特点
				右表为基准匹配右表中的数据,若能够匹配则显现,若匹配不上右表正常显示,左表显示为null
*/

-- 左外连接
SELECT * FROM products p LEFT JOIN category c ON p.category_id = c.cid 

-- 左外连接, 查询每个分类下的商品个数
SELECT c.cname,COUNT(p.pid) '商品个数' FROM category c LEFT JOIN products p ON c.cid  = p.category_id GROUP BY c.cname;

-- 右外连接查询
SELECT * FROM products p RIGHT JOIN category c ON p.`category_id` = c.`cid`;

三种连接比较

在这里插入图片描述

子查询

/*
	子查询
		一条select语句的结果,作为另一条select的一部分
	  特点
			子查询放在小括号中
			子查询作为父查询的条件使用
		分类
			where 子查询:将子查询结果,作为父查询的比较条件
			from 子查询:将子查询结果作为一张表使用
			exists 子查询:查询结果为单列多行情况下,可以将子查询结果作为父查询的in函数中的条件使用
*/
-- 查询价格最高的商品信息
-- 1.查询出最高的价格
SELECT MAX(price) FROM products;
-- 2.根据最高价格查出商品信息
SELECT * FROM products WHERE price = 5000;
-- 使用一条SQL完成
SELECT * FROM products WHERE price = (SELECT MAX(price) FROM products);


-- 子查询做条件
-- 查询化妆品分类下的 商品名称 商品价格
SELECT p.pname,p.price FROM products p WHERE p.category_id = (SELECT c.cid FROM category c WHERE c.cname = '化妆品');
-- 查询小于平均价格的商品信息
SELECT * FROM products p WHERE p.price < (SELECT AVG(price) FROM products);


-- 子查询作为一张表
-- 语法格式:SELECT 查询字段 FROM (子查询)表别名 WHERE 条件;
-- 查询商品中,价格大于500的商品信息,包括 商品名称 商品价格 商品所属分类名称
-- 注意:子查询的结果作为一张表使用,一定要起一个别名,否则无法访问表中字段
SELECT p.pname,p.price,c.cname FROM products p INNER JOIN (SELECT * FROM category) c ON p.category_id = c.cid WHERE p.price > 500;


-- 子查询结果是单列多行
-- 子查询的结果类似一个数组, 父层查询使用 IN 函数 ,包含子查询的结果
-- 语法格式:SELECT 查询字段 FROM 表 WHERE 字段 IN (子查询);
-- 查询价格小于两千的商品,来自于哪些分类(名称)
## 1.查询小于两千的商品的分类id
SELECT DISTINCT category_id FROM products WHERE price < 2000;
## 2.根据分类id查询分类信息
SELECT * FROM category WHERE cid IN (SELECT DISTINCT category_id FROM products WHERE price < 2000);

-- 查询家电类 与 鞋服类下面的全部商品信息
## 1.获取家电类和鞋服类的分类id
SELECT cid FROM category WHERE cname IN ('家电','鞋服');
## 2.根据分类id查找商品
SELECT * FROM products WHERE category_id IN (SELECT cid FROM category WHERE cname IN ('家电','鞋服'));


/*
	子查询总结
		子查询如果是一个字段(单列),那么就在where后面做条件
		如果是多个字段(多列),就当作一张表使用(起别名)
*/

数据库三范式

概念: 三范式就是设计数据库的规则

规则

为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式
满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF) , 其余范式以此类推。一般说来,数据库只需满足第三范式(3NF)就行了

第一范式 1NF

原子性, 做到列不可拆分
第一范式是最基本的范式。数据库表里面字段都是单一属性的,不可再分, 如果数据表中每个字段都是不可再分的最小数据单元,则满足第一范式

在这里插入图片描述

第二范式 2NF

在第一范式的基础上更进一步,目标是确保表中的每列都和主键相关
一张表只能描述一件事

在这里插入图片描述

第三范式 3NF

消除传递依赖
表的信息,如果能够被推导出来,就不应该单独的设计一个字段来存放

在这里插入图片描述

数据库反三范式

反范式化指的是通过增加冗余或重复的数据来提高数据库的读性能
浪费存储空间,节省查询时间 (以空间换时间)

冗余字段

设计数据库时,某一个字段属于一张表,但它同时出现在另一个或多个表,且完全等同于它在其本来所属表的意义表示,那么这个字段就是一个冗余字段

总结

创建一个关系型数据库设计,我们有两种选择
1,尽量遵循范式理论的规约,尽可能少的冗余字段,让数据库设计看起来精致、优雅、让人心醉。
2,合理的加入冗余字段这个润滑剂,减少join,让数据库执行性能更高更快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值