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,让数据库执行性能更高更快。