MySQL数据库(十三)__2018.11.17

多表联合查询。

那在之前的课程中我们已经将了单表的这样一个查询,那有的时候呢,我们的记录不止在一个数据标中,可能在多个表里。这时候就涉及一个多表联查的一个效果。

多表查询或者叫联接查询。首先在多表联接查询中,有以下几种形式。

1.笛卡尔积的形式:用的比较的少,我们可以来看一下。

2.内连接的形式:

3.外连接的形式:

#创建数据库test2
CREATE DATABASE IF NOT EXISTS test2 DEFAULT CHARACTER SET 'utf8';
USE test2;
#创建员工表
CREATE TABLE emp(
	id INT UNSIGNED AUTO_INCREMENT KEY,
	username VARCHAR(20) NOT NULL UNIQUE COMMENT '姓名',
	age TINYINT UNSIGNED NOT NULL DEFAULT 18 COMMENT '年龄',
	sex ENUM('男','女','保密')NOT NULL DEFAULT '保密' COMMENT '性别',
	addr VARCHAR(20) NOT NULL DEFAULT '北京',
	depId TINYINT UNSIGNED NOT NULL COMMENT '部门对应的编号'
)ENGINE=INNODB CHARSET=utf8;

INSERT emp(username,age,depId)VALUES('king',24,1),
('queen',25,2),
('imooc',26,1),
('lily',27,1),
('rose',28,3),
('john',29,3);

#创建部门表
CREATE TABLE dep(
	id TINYINT UNSIGNED AUTO_INCREMENT KEY,
	depName VARCHAR(50) NOT NULL UNIQUE,
	depDesc VARCHAR(100) NOT NULL
)ENGINE=INNODB CHARSET=utf8;

#创建一个公司
INSERT dep(depName,depDesc)VALUES('PHP教学部','研发PHP课件'),
('JAVA教学部','研发JAVA课件'),
('WEB前端教学部','研发WEB前端课件'),
('IOS教学部','研发IOS课件');

报的第一个错误,id有歧义,因为在两个表中都有id它不知道你要查的是哪个id,所以呢我们来指定一下它是哪个表。

这个就是笛卡尔积的形式,显然它这样是不能满足我们的要求的。

有24条记录,那么是怎样得到这么一个结果的呢:有两个表分别为6和4,它们的积就是24了。这就是笛卡尔积的形式了。

它是怎么样来做的呢,先是第一个用户到另外一个表中去查询一下它的部门,同样的是第一个用户再去查它的第二个部门,第三个,第四层。相当于是一个两层循环的形式。

这不是我们想要的结果,那么就要考虑我们另外的两种形式。

内链接和外连接,那么不管是内连接还是外连接,最重要的是考虑两个数据表之间的关系。

内容分别存在两个表中。而这两个表之间又是有联系的。那么这就是我们关系型数据库的一个特点,表和表之间是有关系的。

那么我们来看一下,这两个表中之间有什么联系呢?

我们可以看到,在员工表中存了部门id depId,对应着部门表中的id,这是这两个数据表之间的关系。

那么我们现在想查得时候就可以带上我们的条件了。

先来看一下内连接,内连接查询的时候是用的比较多的。

内连接查询:查询两个数据表中符合连接条件的记录。

那它的语法呢:select  字段名称,...from tb_name1

inner join tn_name2

on 连接条件;

on就是我们两个表连接的桥梁。

通过on来连接两个数据表,这就是两个数据表之间的关系,

这就是我们的一个内连接查询。查询出来的就是符合条件的结果。

关键就是要找准两个数据表之间的关系

你这块可以连接上3个4个...个表,但是一般我们不会超过3三个表,在进行内连接查询的时候。我们涉及到优化的时候不会超过三个表,如果超过三个表,我们的效率是比较低的。我们会进行一个优化,来分别进行查询,在进行一个组合。

然后你看一下,这才是符合我们要求的一个结果

接着是查询另一个表中的内容,要查询这么多内容,那不管你要查询多少内容,结果都是一样的。

找准表和表之间的关系就可以了。

#查询emp id username age addr dep id depName depDesc
SELECT e.id,e.username,e.age,e.addr,
d.id,d.depName,d.depDesc
FROM dep AS d
JOIN emp AS e
#连接条件
ON d.id=e.depId;

通过外键就可以保证这样的数据不能写成功。当然了正常情况下这样的数据也不可能让你写入成功的。因为这样我们数据的一致性就不一致了。这就相当于脏数据。后面呢,我们也可以通过外键,保证这样的数据写入不成功。

#插入一条测试数据
INSERT emp (username,age,depId)
VALUE('测试用户',39,6);

这样的插入记录虽然可以插入,但它破坏的数据的一致性。

内链接会查询出符合两个表中连接条件的一个记录。是那个交集部分。

外连接分为:左外连接和右外连接。

左外连接:select 字段名称,...from tb_name1

outer left join tn_name2

on 连接条件;

左连接查询会先显示左表中的全部记录,再去右表中查询符合条件的记录,不符合的以空值代替。

右外连接:select 字段名称,...from tb_name1

outer right join tn_name2

on 连接条件;

右连接查询会先显示右表中的全部记录,再去左表中查询符合条件的记录,不符合的以空值代替。

左连接查询以左表为主,右连接查询以右表为主。

#测试左外连接
SELECT e.id,e.username,e.age,e.addr,
d.depName,d.depDesc
FROM emp AS e
#outer可以省略
LEFT OUTER JOIN dep AS d
ON e.depId=d.id;

可以看到7个用户都显示出来了。

只有第7个用户没有对应的部门。

#测试右外连接
#yejius右边的表为主表,坐边的表为从表
SELECT e.id,e.username,e.age,e.addr,
d.depName,d.depDesc
FROM emp AS e
#outer可以省略
RIGHT JOIN dep AS d
ON e.depId=d.id;

可以看到右边的表里右空表。

右表的表为主表,也就是说右边的表里的数据我们都能看得到。

所以对于的这样的查询的结果是没有意义的,后面我们可以通过,外键约束,将这样的结果去除掉。

右连接查询,就是先将右边主表的中的记录查询出来放到这里,再从左边从表中查询符合条件的记录,放到这里。

当然了,外连接没有我们的内连接用的多,内连接是用的最多的。

你需要把内连接好好掌握一下。

下面我们多测试一下这种多表联合查询。

上节课中我们讲了,两个表的联合查询,那么就有同学会想了,我们为什么要将数据放在两个表中呢。

#创建一个用户表
CREATE TABLE USER(
	id TINYINT UNSIGNED AUTO_INCREMENT KEY,
	username VARCHAR(20)NOT NULL UNIQUE,
	email VARCHAR(50)NOT NULL DEFAULT '[email protected]',
	proName VARCHAR(10)NOT NULL DEFAULT '北京'COMMENT '省份'
);
INSERT USER(username,proName)VALUES('a','北京'),
('b','哈尔滨'),
('c','上海'),
('d','深圳'),
('e','广州'),
('f','重庆');

这样直接存在一张表中,当需要更新数据表时,会比较麻烦,而且性能会比较低。

比如,我们现在哈尔滨不叫哈尔滨了,改名大东北,如果是在一张表中,会非常麻烦,而如果我们是在两张表中呢?我们来看一下,两张表的情况。

这就是为什么我们要把数据存在两个数据表中。

#两个表的情况
#先创建一个省份表
CREATE TABLE provinces(
	id TINYINT NOT NULL AUTO_INCREMENT KEY,
	proName VARCHAR(10)NOT NULL UNIQUE
);

INSERT provinces(proName)VALUES('北京'),
('上海'),
('深圳');

#创建用户表
CREATE TABLE USER(
	id TINYINT UNSIGNED AUTO_INCREMENT KEY,
	username VARCHAR(20)NOT NULL UNIQUE,
	email VARCHAR(50)NOT NULL DEFAULT '[email protected]',
	proId TINYINT UNSIGNED NOT NULL
);

INSERT USER(username,proId)VALUES('a',1);
INSERT USER(username,proId)VALUES('b',1);
INSERT USER(username,proId)VALUES('c',1);
INSERT USER(username,proId)VALUES('d',2);
INSERT USER(username,proId)VALUES('e',3);
INSERT USER(username,proId)VALUES('f',1);
INSERT USER(username,proId)VALUES('g',1);

#查询user id ,username province proName
SELECT u.id,u.username,p.proName
FROM USER AS u
JOIN provinces AS p
ON u.proId=p.id;

如果我们现在北京不想叫北京了,改名为首都,只用该省份表中的一条记录就可以了。

那么对于前面的要改变几百万的用户,性能是会提高很多的。

后面如果你不想要首都了,就可直接从provinces表中进行删除,而user表是不用删除的,但是以想想如果首都都不存在了,你说用户表中的用户来自首都,那显然也是不可能的。那么这种情况我们就可以通过外键来进行一个约束。把这种情况来给它消除掉。

这就说明了,我们为什么要把它们分开来存储呢。

这就涉及了我们MySQl建表时的一个范式。但是在数据表的优化时你可能会听到反范式,这是为了减少一个我们数据的冗余,提高查询速度。

#创建一个管理员表
#admin id username Email proid
CREATE TABLE admin(
	id TINYINT UNSIGNED AUTO_INCREMENT KEY,
	username VARCHAR(20)NOT NULL UNIQUE,
	email VARCHAR(50)NOT NULL DEFAULT '[email protected]',
	proId TINYINT UNSIGNED NOT NULL
);
#商品分类 cate id cateName cateDesc
#先有商品分类表,然后才能在商品分类表下面创建商品表
CREATE TABLE cate(
	id TINYINT UNSIGNED AUTO_INCREMENT KEY,
	cateName VARCHAR(50)NOT NULL UNIQUE,
	cateDesc VARCHAR(100)NOT NULL DEFAULT ''
);
#商品表product id productName,price,cateId
CREATE TABLE products(
	id INT UNSIGNED AUTO_INCREMENT KEY,
	productName VARCHAR(50)NOT NULL UNIQUE,
	price FLOAT(8,2)NOT NULL DEFAULT 0,
	cateId TINYINT UNSIGNED NOT NULL
);

#添加两个管理员
INSERT admin(username,proId)VALUES('king',1);
INSERT admin(username,proId)VALUES('queen',2);
#添加两个商品分类
INSERT cate(cateName)VALUES('母婴');
INSERT cate(cateName)VALUES('服装');
INSERT cate(cateName)VALUES('电子');
#接下来到了我们的商品了
#商品表product id productName,price,cateId
CREATE TABLE products(
	id INT UNSIGNED AUTO_INCREMENT KEY,
	productName VARCHAR(50)NOT NULL UNIQUE,
	price FLOAT(8,2)NOT NULL DEFAULT 0,
	cateId TINYINT UNSIGNED NOT NULL,
	#将管理员的Id也添加进来,将来产品出了问题,就可以通过管理员id查找到是谁添加进来的
	adminId TINYINT UNSIGNED NOT NULL
);
INSERT products(productName,price,cateId,adminId)
VALUES('iphone9',9883,3,1),
('adidas',388,2,2),
('nike',888,2,2),
('奶瓶',288,1,1);

#查询products id productname price
#先把你要查询的字段列举到这里
SELECT p.id,p.productName,p.price,c.cateName
FROM products AS p
JOIN cate AS c
ON p.cateId=c.id;

#查询管理员 id username email provinces proName
SELECT a.id,a.username,a.email,p.proName
FROM admin AS a
JOIN provinces AS p
ON a.proId=p.id;

接下来我们就可以写三个表的了。

#首先我要查询商品表中商品的编号,名字,价格,还要查询商品的分类,还要查询商品是哪个管理员发布的
#你会发现要查三张表
SELECT p.id,p.productName,p.price,c.cateName,a.username,a.email
FROM products AS p
JOIN admin AS a
ON p.adminId=a.id
JOIN cate AS c
ON p.cateId=c.id;

不论几个表效果都是一样的,关键是找准表和表之间的关系就可以了。

#首先我要查询商品表中商品的编号,名字,价格,还要查询商品的分类,还要查询商品是哪个管理员发布的
#你会发现要查三张表
SELECT p.id,p.productName,p.price,c.cateName,a.username,a.email
FROM products AS p
JOIN admin AS a
ON p.adminId=a.id
JOIN cate AS c
ON p.cateId=c.id
#添加条件商品价格小于1000
WHERE p.price<=1000
#查询完之后,我们还要求按照商品价格降序进行一个排序
ORDER BY price DESC
#查询完之后,我还想限制一下结果集的显示条数,
#假如说我只想显示结果集的两条结果
LIMIT 0,2;
#就完成了一个复合查询,包括了条件呀,多表联合查询,排序呀,限制结果集的显示条数
#我们讲过不要使用超过3个表的联合查询,会影响性能它没有从单表中查询出来再进行合并的效率高

自己练习(四表联查)。

#products id productName price
#cate cateName
#admin username eamil
#provines proName
SELECT p.id AS ' 编号',p.productName AS '商品名称',p.price AS '商品价格',
c.cateName AS '商品分类',
a.username AS '管理员名字',a.email AS '管理员邮箱',
pro.proName AS '省份'
FROM products AS p
JOIN cate AS c
ON p.`cateId`=c.`id`
JOIN admin AS a
ON p.`adminId`=a.`id`
JOIN provinces AS pro
ON a.`proId`=pro.`id`;

猜你喜欢

转载自blog.csdn.net/weixin_40316053/article/details/84176822