本章目录
7.1 内连接查询
7.1.1 交叉连接(笛卡尔积)
7.1.2 内连接查询概要
7.1.3 内连接案例
7.1.4 自然连接
7.2 多表连接查询
7.2.1 多表连接查询方法
7.2.2 多表连接查询应用
7.2.3 简单多表连接查询
7.3 外连接查询
7.3.1 外连接查询概念
7.3.2 左外连接(LEFT JOIN)
7.3.3 右外连接(RIGHT JOIN)
7.4 连接查询综合应用
7.4.1 连接查询中的分组统计
总结:
查询信息的来源如果来自多张表,则必须对这些表进行连接查询。
连接是把不同表的记录连到一起的最普遍的方法,通过连接查询可将多个表作为一个表进行处理。
连接查询分为内连接和外连接。
交叉连接(Cross Join),又称“笛卡尔连接(Cartesian Join)”或“叉乘(Product)”,它是所有类型的内连接的基础。
如果把表视为行记录的集合,那么交叉连接即返回这两个集合的笛卡尔积,返回到结果集合中的数据行数等于第一张表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
示例:实现线路表与车辆表笛卡尔积。
select * from line CROSS JOIN vehicle
等价于:
select * from line,vehicle
内连接基于连接谓词,它将两张表 ( 如A 和 B) 的列组合在一起,产生新的结果表。
内连接查询会将 A 表的每一行和 B 表的每一行进行比较,并找出满足连接谓词的组合。当连接谓词被满足,A 和 B 中匹配的行会按列组合(并排组合)成结果集中的一行。
内连接分 3 种:交叉连接、相等连接和自然连接。
内连接的语法:
SELECT fieldlist FROM table1 [INNER] JOIN table2
ON table1.column1=table2.column2
[where condition]
语法说明:
- fieldlist:table1 表和 table2 表中的字段列。如果 fieldlist 取两张表所有列,则可用“*”代替,此时会出现连接依据列重复,即 table1 表的 column1 与 table2 表的 column2 为重复列。
- table1 [INNER] JOIN table2:将 table1 表与 table2 表进行内连接,INNER 可省略。
- table1.column1=table2.column2:连接条件,其中 column1 和column2 为table1 表与table2 表的关联列,通常它们为外键列和主键列。
- “inner”可省略。
- “where condition”可省略,它为查询条件表达式。
注意:连接依据的列可能包含 null 值,null 值不与任何值匹配(甚至和它本身)。
示例:使用内连接获取所有的线路信息和车辆信息。
分析:
- 车辆表 vehicle 与线路表 line 存在一个连接依据列——lineID。
- 连接以上两张表,使用内连接并采用“*”作为字段列表
select * from line join vehicle on line.lineID=vehicle.lineID
说明:图中“lineID”和“lineID1”为重复列,它们是连接依据列。
说明:使用内连接获取不属于公交二公司的线路信息和车辆信息。
select * from line join vehicle on line.lineID=vehicle.lineID and company<>' 公交二公司 '
示例:使用内连接获取公交二公司的线路信息和车辆信息。
select * from line join vehicle on line.lineID=vehicle.lineID where company=' 公交二公司 '
说明:
连接查询的 where 条件表达式可以用 on 子句中的条件表达式进行替换,所以本例的 SQL 语句也可以如下:
select * from line join vehicle on line.lineID=vehicle.lineID and company=' 公交二公司 '
示例:使用内连接获取车辆型号含有“DD”字样的车辆信息和司机信息。
select * from vehicle join driver on vehicle.driverID=driver.driverID where model like '%DD%'
示例:使用内连接获取车辆型号含有“DD”字样的车辆信息和司机信息,要求车辆信息为全部列,司机信息中只需包含姓名和身份证。
select v.*, name, licenseNo from vehicle v join driver d on v.driverID=d.driverID where model like '%DD%'
说明:
- 在连接查询中,为了简化字段列表的显示,一般需要为两张表分别取别名,如本例中分别为表“vehicle”和“driver”取别名“v”和“d”。
- 如果字段列表中的字段在两张表中是唯一的,则该字段前面不需要加表名前缀,例如本列中的“name” 和“licenseNo”,否则需要加表名前缀,如本列中的“v.vehicleID” “d.vehicleID”等。
示例:使用内连接获取年龄大于 35 岁的司机信息和该司机所驾驶的车辆信息,要求车辆信息为全部列,司机信息中只需包含姓名、出生年月和电话。
select v.*, name,birthday,phone from vehicle v join driver d on v.driverID=d.driverID where CEIL(DATEDIFF(now(),birthday)/365)>35
自然连接 (Natural Join) 是一种特殊的内连接,它要求相连接的两张表的连接依据列必须是相同的字段(字段名相同,字段属性相同)。
在自然连接中两张表的所有名称相同的列都将被比较,并且在结果集中把重复的列去掉, 结果集中两张表中名称相同的列仅出现一次。而普通的内连接并不去掉重复的列。
部分数据库不支持自然连接, 如 SQL Server 等。
语法:
SELECT fieldlist FROM table1 NATURAL JOIN table2
[where condition]
示例:使用自然连接获取所有的线路信息和车辆信息。
select * from line natural join vehicle
示例:用自然连接获取公交二公司的线路信息和车辆信息。
select * from line natural join vehicle where company=' 公交二公司 '
示例:使用自然连接获取车辆型号含有“DD”字样的车辆信息和司机信息,要求车辆信息为全部列,司机信息中只需包含姓名和身份证。
select v.*, name, licenseNo from vehicle v natural join driver d where model like '%DD%'
如果查询的信息来源于多张表,则可通过两两相连的方式建立多表连接查询。
三表连接查询的语法:
SELECT fieldlist FROM table1 JOIN table2 ON table1.column1=table2.column2_1 JOIN table3 on
table2.column2_2=table3.column3 [where condition]
说明:
table2 与 table1 和 table3 两两相连。
三表以上连接查询的方法与三表连接查询的方法一样,均是通过两两相连的方式实现。
示例:获取所有非空调车的车牌号、型号和司机姓名、所属线路的线路号、起点站和终点站信息。
分析:车牌号和型号来源于车辆表;司机姓名来源于司机表;线路号、起点站和终点站来源于线路表,因而需要连接 3 张表:车辆表、司机表和线路表
select name 司机姓名 , plateNo 车牌号 , model 型号 , lineNo 线路号 , from_station 起点站 , end_station 终点站 from vehicle V join Driver D on V.driverID=D.driverID
join line L on V.lineID=L.lineID where type=' 非空调车 ';
示例:获取公交二公司所有司机信息。要求输出司机姓名、身份证、性别和电话。
分析:司机表与车辆表有关联,车辆表又与线路表有关联,线路表有所属公司信息,所以通过连接司机表、车辆表和线路表,可以获得公交二公司所有司机的相关信息。
select name 司机姓名 , licenseNo 身份证 , gender 性别 , phone 电话 from vehicle V join Driver D on V.driverID=D.driverID join line L on V.lineID=L.lineID
where company=' 公交二公司 '
如果在 FROM 子句中,直接列出所有要连接的表,然后在 WHERE 子句中指定连接条件,此为简单多表查询, 它与内连接功能相同。
使用两表连接查询语法:
SELECT fieldlist FROM table1, table2
WHERE table1.column1=table2.column2 [and 其他条件]
使用三表连接查询语法:
SELECT fieldlist FROM table1,table2,table3
WHERE table1.column1=table2.column2_1 and table2.column2_2=table3.column3 [and 其他条件]
示例:获取所有非空调车的车牌号、型号和司机姓名、所属线路的线路号、起点站和终点站信息。
select name 司机姓名 , plateNo 车牌号 , model 型号 , lineNo 线路号 , from_station 起点站 , end_station 终点站 from vehicle V ,Driver D,line L where V.driverID=D.driverID
and V.lineID=L.lineID and type=' 非空调车 '
示例:获取公交二公司所有司机信息。要求输出司机姓名、身份证、性别和电话。
select name 司机姓名 , licenseNo 身份证 , gender 性别 , phone 电话
from vehicle V, Driver D, line L where V.driverID=D.driverID and V.lineID=L.lineID and company=' 公交二公司 '
在内连接查询中,只有满足连接条件的记录才能出现在查询结果中。但在实际应用中,如果希望不满足连接条件的记录也在查询结果中出现,这时需要使用外连接查询。
根据不同的外连接形式,外连接所生成的结果集中不仅包含符合条件的数据记录,还包含左表或右表或左右表中所有的数据记录。
语法:
SELECT 字段名称 FROM 表名 1 LEFT|RIGHT|FULL [OUTER] JOIN 表名 2
ON 表名 1. 字段名 1= 表名 2. 字段名 2
“outer”可省略
左外连接的结果集包括左表的所有记录和右表中满足连接条件的记录,结果集中那些不符合连接条件的来源于右表的列值为 null。
示例:查询所有非空调车的车牌号、型号、线路号、起点站和终点站。
分析:由于需要显示所有的非空调车,所以只能外连接。如果将车辆表 vehicle 作为连接左表,线路表 line 作为右表, 则该连接为左外连接。
select plateNo 车牌号 , model 型号 , lineNo 线路号 , from_station 起点站 , end_station 终点站from vehicle V left join line L on V.lineID=L.lineID where type=' 非空调车 ';
非匹配行。来源于右表列相应字段为空
示例:显示所有司机基本信息,并查询其所驾驶车辆和行驶线路的相关信息,要求输出司机姓名、性别、电话、车牌号、型号、线路号、起点站和终点站。
分析:连接表为司机表、车辆表和线路表。由于需要显示所有的司机信息,所以需要将司机表和车辆表作为左表(3 张表的连接次序为司机表、车辆表和线路表)。
select name 司机姓名 , gender 性别 , phone 电话 , plateNo 车牌号 , model 型号 , lineNo 线路号 , from_station 起点站 , end_station 终点站 from driver D left join vehicle V
on D.driverID=V.driverID left join line L on V.lineID=L.lineID
右外连接是左外连接的反向连接。右外连接的结果集包括右表的所有记录和左表中满足连接条件的记录,结果集中那些不符合连接条件的来源于左表的列值为 null。
示例:查询所有线路的车辆信息,要求显示车牌号、型号、线路号、起点站和终点站。
分析:由于需要显示所有的线路信息,所有只能外连接。如果车辆表 vehicle 作为连接左表,线路表 line 作为右表, 则该连接为右外连接。
select plateNo 车牌号 , model 型号 , lineNo 线路号 , from_station 起点站 , end_station 终点站from vehicle V right join line L on V.lineID=L.lineID
非匹配行。来源于左表列相应字段为空
示例:显示所有线路及其所配车辆基本信息,并查询车辆所配司机相关信息,要求输出司机姓名、性别、电话、车牌号、型号、线路号、起点站和终点站。
分析:连接表为司机表、车辆表和线路表。由于需要显示所有线路及其所配车辆基本信息,所以需要将线路表和车辆表作为右表(三张表的连接次序为司机表、车辆表和线路表)。
select name 司机姓名 , gender 性别 , phone 电话 , plateNo 车牌号 , model 型号 , lineNo 线路号 , from_station 起点站 , end_station 终点站 from driver D right join vehicle V
on D.driverID=V.driverID right join line L on V.lineID=L.lineID
综合使用连接查询、聚合函数和分组查询可以实现很多复杂的查询需求。
公交线路站点表用于存储每条公交线路与其所属站点之间关联关系:
字段名 |
说 明 |
类 型 |
长 度 |
约 束 |
lineStationID |
公交线路站点编号 |
int |
— |
主键,自动增长 |
lineNo |
线路号 |
varchar |
20 |
外键,参照线路表线路号 |
station |
公交站点 |
varchar |
20 |
— |
示例:统计每一家公交公司所属线路的站点总数。
分析:解决的关键是依据公交公司分组统计该公司所有线路的站点数量,但线路车站表 line_station 中仅有线路信息即线路号 lineNo,却没有该线路所属公司信息,而线路表 line 却有公司信息即公司名 company, 并且这两张表都有线路号 lineNo。所以,可以通过连接 line_station 表和 line 表,并根据 line 表的 company 列分组统计每一家公交公司所属线路的站点总数。
select company 公司 , count(*) 站点数 from line_station LS, line L where LS.lineNo=L.lineNo group by company order by count(*)
示例:计算经过站点“解放大道古田四路”的公交线路的数量以及营运车辆数量。
分析:连接线路站点表和线路表。
select count(*) 线路数 , sum(number) 营运车辆数 from line_station LS, Line L where LS.lineNo=L.lineNo and station=' 解放大道古田四路 '
示例:计算每条公交线路的司机人数,按司机人数逆序显示。
分析:连接驾驶员表、车辆表和线路表。
select lineNo 线路号 , count(*) 司机数 from driver D, vehicle V, line L
where D.driverID=V.driverID and V.lineID=L.lineID group by lineNo order by 司机数 desc
示例:计算每条公交线路的司机人数,并显示司机人数大于 3 的分组信息,按司机人数逆序显示。
分析:仅需在前例的基础之上将司机人数大于 3 的分组数据过滤出来。
select lineNo 线路号 , count(*) 司机数 from driver D, vehicle V, line L where D.driverID=V.driverID and V.lineID=L.lineID group by lineNo having count(*)>3 order by 司机数 desc
内连接基于连接谓词,它将两张表(如 A 和 B)的列组合在一起,产生新的结果表。具体过程是将 A 表的每一行和 B 表的每一行进行比较,并找出满足连接谓词的组合,当连接谓词被满足,A 和 B 中匹配的行会按列组合(并排组合)成结果集中的一行。
内连接分 3 种:交叉连接、相等连接和自然连接。
SELECT fieldlist FROM table1 JOIN table2 ON table1.column1=table2.column2_1 JOIN table3 on table2.column2_2=table3.column3 [where condition]
SELECT fieldlist FROM table1,table2,table3 where table1.column1=table2.column2_1 and table2.column2_2=table3.column3 [and 其他条件]
在内连接查询中,只有满足连接条件的记录才能出现在查询结果中。但在实际应用中,如果希望不满足连接条件的记录也在查询结果中出现,这时需要使用外连接查询。根据不同的外连接形式,外连接所生成的结果集中不仅包含符合条件的数据记录,还包含左表、右表或左右表中所有的数据记录。
左外连接的结果集包括左表的所有记录和右表中满足连接条件的记录,结果集中那些不符合连接条件的来源于右表的列值为 null。