mysql多表之间的操作
1.多表之间的维护
外键
定义:外键是指引用另一个表中的一列或多列,被引用的列应该具有主键约束或唯一性约束,外键用于建立和加强两个表数据之间的连接。
语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY(外键字段名) REFERENCES 外表表名(主键字段名);
删除外键:ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
这里有两张表product 和category
mysql> select * from product;
+----+------+-------+-----+
| id | name | price | num |
+----+------+-------+-----+
| 1 | 伊利 | 68 | 1 |
| 2 | 蒙牛 | 88 | 1 |
| 3 | nike | 888 | 2 |
| 4 | 阿迪 | 688 | 2 |
| 5 | kris | 1888 | 3 |
| 6 | tom | 2888 | 3 |
| 7 | sam | 1688 | 3 |
+----+------+-------+-----+
mysql> select * from category;
+-----+-------+---------------------+
| cid | kind | date |
+-----+-------+---------------------+
| 1 | 饮料 | 2019-01-11 15:17:03 |
| 2 | 鞋子 | 2019-01-11 15:17:03 |
| 3 | other | 2019-01-11 15:17:03 |
+-----+-------+---------------------+
3 rows in set
cid 为category的主键,id为product的主键。
上面两张表可以看出cid和num是对应关系,饮料有蒙牛和伊利… 上面的表我们能很清楚的看清商品的分类和信息,那么我们为什么需要添加外键来约束这两张表呢?最直观的作用:防止非法数据的录入(录入外键数据时,必须在它所属的主表中存在相对应的数据),保证数据的参照完整性,一致性!
如何理解?假设上面两张表是超市的数据库中的,在查看product表的时候发现多了一行数据,但是在category中并没有与之对应的kind,那么这就是一条垃圾数据。如果在两个表之间添加外键,后期的维护成本会降低。
外键的作用:拿上面两张表举例:我想在product表中添加一个商品但是category中并没有对应的cid会出错
将product表中的num添加外键引用category的cid
mysql> alter table product add foreign key(num) references category(cid);
Query OK, 7 rows affected
在product表中添加一个商品信息。提示添加失败,这是因为cid并没有4这个值。
mysql> insert into product values(null,'Gucci',6888,4);
1452 - Cannot add or update a child row: a foreign key constraint fails (`student`.`product`, CONSTRAINT `product_ibfk_1` FOREIGN KEY (`num`) REFERENCES `category` (`cid`))
先在category中添加相应的cid
mysql> insert into category values(4,'衣服',null);
Query OK, 1 row affected
然后添加成功
mysql> insert into product values(null,'Gucci',6888,4);
Query OK, 1 row affected
mysql> select * from product;
+----+-------+-------+-----+
| id | name | price | num |
+----+-------+-------+-----+
| 1 | 伊利 | 68 | 1 |
| 2 | 蒙牛 | 88 | 1 |
| 3 | nike | 888 | 2 |
| 4 | 阿迪 | 688 | 2 |
| 5 | kris | 1888 | 3 |
| 6 | tom | 2888 | 3 |
| 7 | sam | 1688 | 3 |
| 9 | Gucci | 6888 | 4 |
+----+-------+-------+-----+
关于建表的时候是否一定要设置外键:https://www.zhihu.com/question/22140799 可以参考这篇回答
2.多表之间的关系
2.1一对一
例:公民和身份证
mysql> select * from citizen;
+----+------+--------+
| id | name | salary |
+----+------+--------+
| 1 | 张三 | 100000 |
| 2 | 李四 | 150000 |
| 3 | 王五 | 200000 |
+----+------+--------+
mysql> select * from infor;
+------+----------+
| num | address |
+------+----------+
| 1001 | 四川重庆 |
| 1002 | 山东青岛 |
| 1003 | 广东广州 |
+------+----------+
建表原则:可以将一对一的关系看成一对多,将infor当做成‘多’的一方。在infor表中添加一个列对应与citizen表中的id列,此列必须设置成unique(citizen是一一对应的关系,不设置成unique的话就变成一对多)
mysql> select * from infor;
+------+----------+-----+
| num | address | cid |
+------+----------+-----+
| 1001 | 四川重庆 | 1 |
| 1002 | 山东青岛 | 2 |
| 1003 | 广东广州 | 3 |
+------+----------+-----+
infor中的cid设置外键
mysql> alter table infor add foreign key(cid) references citizen(id);
Query OK, 3 rows affected
Records: 3 Duplicates: 0 Warnings: 0
mysql> show create table infor;
| infor | CREATE TABLE `infor` (
`num` int(11) NOT NULL,
`address` varchar(10) DEFAULT NULL,
`cid` int(11) DEFAULT NULL,
PRIMARY KEY (`num`),
UNIQUE KEY `cid` (`cid`),
CONSTRAINT `infor_ibfk_1` FOREIGN KEY (`cid`) REFERENCES `citizen` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
2.2一对多
如上面多表之间的维护中的例子
2.3多对多
例:学生和课程 一个学生可以选择多个课程,一个课程也可以被多个学生选择。
mysql> select * from course;
+----+------------+---------+
| id | cname | teacher |
+----+------------+---------+
| 1 | Java | 李老师 |
| 2 | C++ | 王老师 |
| 3 | Python | 吴老师 |
| 4 | JavaScript | 张老师 |
+----+------------+---------+
mysql> select * from pupil;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | 张三 | 25 |
| 2 | 李四 | 23 |
| 3 | 王五 | 18 |
| 4 | 麻子 | 27 |
+----+------+-----+
**如何建立关系:**多建一张表,这张表中至少2个外键,这两个外键分别指向原来的表,将多对多转化为一对多。
学生和课程的关系如下:
张三:Java C++ JavaScript
李四:python JavaScript
王五:C++ python
麻子:Java python
建立一张关联表,将pid设置为外键指向pupil的主键id,cid设置为外键指向course的主键id。
mysql> create table relation(
-> pid int,
-> cid int,
-> foreign key(pid) references pupil(id),
-> foreign key(cid) references course(id));
Query OK, 0 rows affected
将学生课程关系添加进关联表relation
mysql> insert into relation values(1,1),(1,2),(1,4),(2,3),(2,4),(3,2),(3,3),(4,1),(4,3);
Query OK, 9 rows affected
Records: 9 Duplicates: 0 Warnings: 0
插入错误数据
mysql> insert into relation values(5,1);
1452 - Cannot add or update a child row: a foreign key constraint fails (`student`.`relation`, CONSTRAINT `relation_ibfk_1` FOREIGN KEY (`pid`) REFERENCES `pupil` (`id`))
3.多表之间的查询
上面是将多表关联起来,那么如何查询多表连接的数据的?
3.1内连接
内连接使用比较运算符对两个表中的数据进行比较,并列出与连接条件匹配的数据行,组成新的记录。
语法: select 数据 from tb1 inner join tb2 on 条件 [where条件];
例:查询公民的信息
mysql> select * from citizen inner join infor on citizen.id=infor.cid;
+----+------+--------+------+----------+-----+
| id | name | salary | num | address | cid |
+----+------+--------+------+----------+-----+
| 1 | 张三 | 100000 | 1001 | 四川重庆 | 1 |
| 2 | 李四 | 150000 | 1002 | 山东青岛 | 2 |
| 3 | 王五 | 200000 | 1003 | 广东广州 | 3 |
+----+------+--------+------+----------+-----+
添加一个where条件取第一条数据
mysql> select * from citizen inner join infor on infor.cid=citizen.id where citizen.id=1;
+----+------+--------+------+----------+-----+
| id | name | salary | num | address | cid |
+----+------+--------+------+----------+-----+
| 1 | 张三 | 100000 | 1001 | 四川重庆 | 1 |
+----+------+--------+------+----------+-----+
3.2交叉连接
交叉连接返回的结果是被连接的两个表中所有数据行的笛卡尔集,也即返回第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
语法:select 数据 from tb1 cross join tb2 [where条件];
例:查询所有公民的信息和地址信息;
mysql> select c.name,i.address from citizen c cross join infor i;
+------+----------+
| name | address |
+------+----------+
| 张三 | 四川重庆 |
| 李四 | 四川重庆 |
| 王五 | 四川重庆 |
| 张三 | 山东青岛 |
| 李四 | 山东青岛 |
| 王五 | 山东青岛 |
| 张三 | 广东广州 |
| 李四 | 广东广州 |
| 王五 | 广东广州 |
+------+----------+
3.3外连接
外连接分为左连接和右连接,当返回的查询结果不仅需要包含符合条件的数据,还需要包含其中一个表或者两个表的所有数据的时候,需要用到外连接查询。
语法:select 数据 from tb1 left/right join tb2 on tb1.字段=tb2.字段 [where条件];
左连接:left join返回左表的所有记录和右表中符合条件的数据,不符合的条件数据用null填充。
mysql> select * from product p left outer join category c on p.num=c.cid;
+----+----------+-------+-----+-----+-------+---------------------+
| id | name | price | num | cid | kind | date |
+----+----------+-------+-----+-----+-------+---------------------+
| 1 | 伊利 | 68 | 1 | 1 | 饮料 | 2019-01-11 15:17:03 |
| 2 | 蒙牛 | 88 | 1 | 1 | 饮料 | 2019-01-11 15:17:03 |
| 3 | nike | 888 | 2 | 2 | 鞋子 | 2019-01-11 15:17:03 |
| 4 | 阿迪 | 688 | 2 | 2 | 鞋子 | 2019-01-11 15:17:03 |
| 5 | kris | 1888 | 3 | 3 | other | 2019-01-11 15:17:03 |
| 6 | tom | 2888 | 3 | 3 | other | 2019-01-11 15:17:03 |
| 7 | sam | 1688 | 3 | 3 | other | 2019-01-11 15:17:03 |
| 9 | Gucci | 6888 | 4 | 4 | 衣服 | 2019-01-11 15:54:05 |
| 10 | 海澜之家 | 1888 | 4 | 4 | 衣服 | 2019-01-11 15:54:05 |
+----+----------+-------+-----+-----+-------+---------------------+
如果将第10条数据中的num=5,那么第10条数据不符合条件 num=cid,则结果是
| 10 | 海澜之家 | 1888 | 5 | null | null | null |
右连接:right join和左连接相反。
4.子查询
一个查询语句嵌套在另一个查询语句内。分为相关子查询和嵌套子查询,有关子查询和主查询的执行顺序看看这篇文章:https://blog.csdn.net/qq_38238296/article/details/86251165
4.1 in
例:获取product中num和category的cid相等的商品
mysql> select * from product;
+----+----------+-------+-----+
| id | name | price | num |
+----+----------+-------+-----+
| 1 | 伊利 | 68 | 1 |
| 2 | 蒙牛 | 88 | 1 |
| 3 | nike | 888 | 2 |
| 4 | 阿迪 | 688 | 2 |
| 5 | kris | 1888 | 3 |
| 6 | tom | 2888 | 3 |
| 7 | sam | 1688 | 3 |
| 9 | Gucci | 6888 | 4 |
| 10 | 海澜之家 | 1888 | 5 |
+----+----------+-------+-----+
9 rows in set
mysql> select * from product p where num in(select cid from category c where p.num=c.cid);
+----+-------+-------+-----+
| id | name | price | num |
+----+-------+-------+-----+
| 1 | 伊利 | 68 | 1 |
| 2 | 蒙牛 | 88 | 1 |
| 3 | nike | 888 | 2 |
| 4 | 阿迪 | 688 | 2 |
| 5 | kris | 1888 | 3 |
| 6 | tom | 2888 | 3 |
| 7 | sam | 1688 | 3 |
| 9 | Gucci | 6888 | 4 |
+----+-------+-------+-----+
8 rows in set
不在这个范围的商品 :NOT IN
mysql> select * from product p where num not in(select cid from category);
+----+----------+-------+-----+
| id | name | price | num |
+----+----------+-------+-----+
| 10 | 海澜之家 | 1888 | 5 |
+----+----------+-------+-----+
1 row in set
in如何进行查询的:in在查询的时候,首先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选。 也就是说上面的语句是先将category中的cid字段全部遍历缓存起来,然后检查并筛选出product表中的num与cid相等的数据。in需要将product表和category表中的数据全部遍历一次!
in()只执行一次:将in()中的遍历并缓存出来
4.2 exists
EXISTS 关键字后面的子查询不产生任何数据,只返回 TRUE 或 FALSE,当返回 TRUE 时,外层查询才会执行。
例:查询category中是否有’衣服’这一种类,如果存在返回product对应的商品信息
mysql> select * from product p where exists (select 1 from category c where p.num=c.cid and c.cid=4);
+----+----------+-------+-----+
| id | name | price | num |
+----+----------+-------+-----+
| 8 | gucci | 6888 | 4 |
| 9 | 海澜之家 | 1888 | 4 |
+----+----------+-------+-----+
2 rows in set
这里的select 1 的意思返回一条数据。
EXISTS(subquery)只返回TRUE或者FALSE,因此子查询中的SELECT * 也可以是SELECT 1或者SELECT 'X',官方说法是实际执行时会忽略SELECT清单,因此没有区别。
select 1 from table;与select anycol(目的表集合中的任意一行) from table;与select * from table 从作用上来说是没有差别的,都是查看是否有记录,一般是作条件查询用的。select 1 from 中的1是一常量(可以为任意数值),查到的所有行的值都是它,但从效率上来说,1>anycol>*,因为不用查字典表。
执行原理:exists先对外表做loop循环,每次循环对内表进行查询,若成功匹配则exists返回true,将元素输出。也就是说将外表中的每行元素,进行exists(subquery)条件匹配。
执行次数:外表.length。
注意:in后面子查询可以使用任何子查询(或常数),exists后面的只能是相关子查询(不然没意义)。
具体有关 IN /NOT IN /EXISTS /NOT EXISTSl之间的区别可以看这篇文章:https://blog.csdn.net/qq_38238296/article/details/86601799