目录
多表连接查询是通过各个表之间共同列的关联性(例如:外键)来查询的
多表连接查询的分类
- 内连接(INNER JOIN) ,可简写为 JOIN;
- 左外连接(LEFT OUTER JOIN),可简写为LEFT JOIN;
- 右外连接(RIGHT OUTER JOIN),可简写为RIGHT JOIN;
- 自连接查询;
交叉连接(CROSS JOIN)也称为 笛卡儿乘积连接,基本上用不到。
内连接
内连接:内连接使用比较运算符根据每个表的通用列中的值匹配两个表中的行。
写法1
SELECT 字段列表 FROM 表1 INNER JOIN 表2
ON 表1.通用字段名=表2.通用字段名
写法2
#第二种写法
SELECT 字段列表 FROM 表1 ,表2
WHERE 表1.通用字段名=表2.通用字段名
- 如果两个表中有相同的字段名,必须在列名前加表名。否则将出现 错误代码:1052 Column ‘xx’ in field list is ambiguous
代码示例
#第一种写法
SELECT s.studentno,s.studentname,r.studentresult FROM
student AS s INNER JOIN result AS r
ON s.studentno=r.studentno ORDER BY r.studentresult DESC
#第二种写法
SELECT s.studentno,s.studentname,r.studentresult FROM
student AS s,result AS r
WHERE s.studentno=r.studentno
ORDER BY r.studentresult DESC
自连接
案例分析1:存储
某一电商网站想要对站内产品做层级分类,一个类别下面有若干子类,子类下面也会有别的子类。例如数码产品这个类别下面有笔记本,台式机,智能手机等;笔记本,台式机,智能手机又可以按照品牌分类等等。也许这些分类会达到一个很深的层次,呈现一种树状的结构。那么这些数据要怎么在数据库中表示呢
表结构
我们可以在数据库中创建两个字段来存储id和类别名称,使用第三个字段存储类别的子类或者父类的id,最后通过自连接去查询想要的结果。
CREATE TABLE IF NOT EXISTS tbl_category(
cid int(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
cname VARCHAR(50) NOT NULL ,
pid INT(10) );
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (1, '电子产品', NULL);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (2, '服装', NULL);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (3, '百货', NULL);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (4, '母婴', NULL);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (5, '食品', NULL);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (6, '手机', 1);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (7, '电脑', 1);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (8, '相机', 1);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (9, '阿迪达斯', 2);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (10, '班尼路', 2);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (11, '口罩', 3);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (12, '花露水', 3);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (13, '沐浴露', 3);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (14, '奶粉', 4);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (15, '奶瓶', 4);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (16, '纸尿裤', 4);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (17, '隔尿垫', 4);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (18, '辣条', 5);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (19, '花生', 5);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (20, '大米', 5);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (21, '瓜子', 5);
INSERT INTO `chinanewsdb`.`tbl_category` (`cid`, `cname`, `pid`) VALUES (22, '大枣', 5);
案例分析2:查询
假想以下场景:插入测试数据后,如何查询所有分类以及分类的父类呢 ?
SQL案例
自连接查询其实等同于连接查询,需要两张表,只不过它的左表(父表)和右表(子表)都是自己。做自连接查询的时候,是自己和自己连接,分别给父表和子表取两个不同的别名,然后附上连接条件。
SELECT c1.cname AS "父名称",c2.cname AS "子名称"
FROM tbl_category AS c1, tbl_category AS c2
WHERE c1.cid = c2.pid;
左外连接
左外连接:左表(表1)的记录将会全部表示出来,而右表(表2)只会显示符合匹配条件的记录。
- 匹配,返回到结果集
- 无匹配,NULL值返回到结果集
语法
SELECT 字段列表 FROM 表1 LEFT JOIN 表2 ON 表1.通用字段名=表2.通用字段名
案例
查询学生的学号、姓名和成绩,并按照成绩降序排列
SQL示例
SELECT s.studentno,s.studentname,r.studentresult FROM
student AS s
LEFT JOIN
result AS r
ON s.studentno=r.studentno
ORDER BY r.studentresult DESC;
右外连接
右外连接:右表(表2)的记录将会全部表示出来,而左表(表1)只会显示符合匹配条件的记录。
- 匹配,返回到结果集
- 无匹配,NULL值返回到结果集
语法
SELECT 字段列表 FROM 表1 RIGHT JOIN 表2 ON 表1.通用字段名=表2.通用字段名
案例
查询学生的学号、姓名和成绩,并按照成绩降序排列
SQL示例
SELECT s.studentno,s.studentname,r.studentresult FROM
result AS r
RIGHT JOIN
student AS s
ON s.studentno=r.studentno
ORDER BY r.studentresult DESC;
连接查询中on条件与where条件的区别
以left join为例:
- on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。
- where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。
案例分析
tb1:
no | price |
---|---|
1 | $10 |
2 | $20 |
3 | $30 |
4 | $40 |
tb2:
name | no |
---|---|
黄瓜 | 1 |
西瓜 | 2 |
草莓 | 3 |
SQL1分析
select * from tb1 left join tb2 on tb1.no= tb2.no where tb2.name='黄瓜';
- 第一步:连接。连接条件: tb1.no= tb2.no
- 第二步:再对中间表过滤。where 条件:tb2.name=’黄瓜’
SQL2分析
select * from tb1 left join tb2 on tb1.no= tb2.no and tb2.name=’黄瓜';
-
第一步:对右表做连接前的筛选: tb2.name=’黄瓜’
-
第二步:连接。连接条件:tb1.no=tb2.no (条件不为真也会返回左表中的记录)
小结
在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里