为什么要有多表
最直观的感受是减少冗余。比如工作地点,通常工作地点也就那几个地方,每个员工都存一个很长的字符串就很不方便了。可以考虑拆成两张表。
多表环境下,有外键的表叫做从表,外键对应的表叫做主表。
外键约束
防止从表随便插入
/* 添加外键 方式1 建表时给出 create table xxx ( 字段... [constraint] [外键约束名] foreign key(外键字段名) references 主表(主键字段) ); */ 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 ); /* 增加数据 先增加主表 删除数据 先删除从表 */ # 删除外键 ALTER TABLE employee DROP FOREIGN KEY emp_dept_fk; # 添加外键 ALTER TABLE employee ADD CONSTRAINT emp_dept_fk FOREIGN KEY (dept_id) REFERENCES department(id); # 级联删除 # 可以在删除主表数据时删除与之相关的从表的数据
多表之间的关系
一对一 人和身份证
一对多 班级和学生 多的那一方加外键即可
多对多 学生和课程 需要中间表,中间表中的是外键
多表查询
/* 会产生笛卡尔积 */ SELECT * FROM products, category;
内连接
注意,连接本身与逻辑无关。你where条件怎么写,和你多表查询要连接是没关系的。
# 隐式内连接 用where指定连接条件 # 1) 查询所有商品信息和对应的分类信息 SELECT * FROM products, category WHERE category_id = cid; # 2) 查询商品表的商品名称 和 价格,以及商品的分类信息 SELECT p.`pname`, p.`price`, c.`cname` FROM products p, category c WHERE p.category_id = c.cid; # 3) 查询 格力空调是属于哪一分类下的商品 SELECT p.`pname`, c.`cname` FROM products p, category c WHERE c.`cid` = p.`category_id` AND p.`pname` = '格力空调'; # 显式内连接 # 1) 查询所有商品信息和对应的分类信息 SELECT * FROM products p INNER JOIN category c ON p.`category_id` = c.`cid`; # 2) 查询鞋服分类下,价格大于500的商品名称和价格 SELECT p.`pname`, p.`price` FROM products p INNER JOIN category c ON p.`category_id` = c.`cid` WHERE p.`price` > 500 AND c.`cname` = '鞋服';
外连接
左外连接以左表为基准,右外连接以右表为基准
# 外连接查询 左外left join SELECT * FROM category c LEFT JOIN products p ON c.`cid` = p.`category_id`; # 查询每个分类下的商品个数 SELECT c.`cname`, COUNT(p.`pid`) FROM category c LEFT JOIN products p ON c.`cid` = p.`category_id` GROUP BY c.`cname`; # 右外连接 right join SELECT * FROM products p RIGHT JOIN category c ON p.`category_id` = c.`cid`;
子查询
子查询一定要放在小括号里。
# 子查询 # 找出最贵的商品的信息 SELECT * FROM products WHERE price = (SELECT MAX(price) FROM products);
子查询分类
where型 子查询结果作为where条件
# 找出最贵的商品的信息 SELECT * FROM products WHERE price = (SELECT MAX(price) FROM products); # 查询化妆品分类下的 商品名称 商品价格 SELECT p.`pname`, p.`price` FROM products p WHERE p.`category_id` = (SELECT cid FROM category WHERE cname = '化妆品'); # 查询小于平均价格的商品信息 SELECT AVG(price) FROM products; SELECT * FROM products WHERE price < (SELECT AVG(price) FROM products);
from型 子查询结果是一张表
SELECT p.`pname`, p.`price`, c.`cname` FROM products p INNER JOIN category c WHERE p.`category_id` = c.`cid` AND p.`price` > 500; SELECT p.`pname`, p.`price`, c.`cname` FROM products p # 注意这个括号里的子查询是个虚拟表,所以必须起个别名 INNER JOIN (SELECT * FROM category) c WHERE p.`category_id` = c.`cid` AND p.`price` > 500;
exists型 子查询结果是单列多行
# 查询价格小于两千的商品,来自于哪些分类(名称) SELECT * FROM category WHERE cid IN (SELECT DISTINCT category_id FROM products WHERE price < 2000); # 查询家电类 与 鞋服类下面的全部商品 SELECT * FROM products WHERE category_id IN (SELECT cid FROM category WHERE cname IN ('家电','鞋服'));
三范式
目的:创建冗余小结构合理的数据库。范式就是设计数据库的要求/规范。
1NF 原子列,列不可以再分。
2NF 确保表中每一列都和主键所在列相关,一张表只描述一件事。
3NF 消除传递依赖。表中的列不应该能被推导出来。
anti-三范式
通过增加冗余来换取性能
冗余字段-> 某个字段属于一张表,但是又在多张表中都有出现。
冗余字段可以减少join的次数。