此章节为重点以及难点
文中标注理解的地方为本人理解,如有错误欢迎大佬指出。
目录
一、多表查询的分类
类别1:等值连接与非等值连接
等值连接
非等值连接
类别2:自连接
类别3:内连接与外连接(重点及难点,特别是外连接)
SQL99的多表查询语法
基本语法
外连接(OUTER JOIN)
UNION操作
去重方法,7种JOIN操作(放大招了)
例如:
代码:
SELECT employees.employee_id, employees.last_name,
employees.department_id, departments.department_id,
departments.location_id
FROM employees, departments
WHERE employees.department_id = departments.department_id;
解释:等值就是以两个表中相同的字段中元素值相等作为连接方式,即为一种一一对应的拼接方式。
补充:
1、表的别名
·使用表的别名可以简化查询
·列名前使用表名前缀可提高查询效率
SELECT e.employee_id, e.last_name, e.department_id,
d.department_id, d.location_id
FROM employees e , departments d
WHERE e.department_id = d.department_id;
2、区分重复的列名(重点
·多个表中有相同列时,必须在列名之前加上表名前缀。
·在不同表中具有相同列名的列可以用表名
加以区分。
SELECT employees.last_name, departments.department_name,employees.department_id
FROM employees, departments
WHERE employees.department_id = departments.department_id;
注意:推荐在任何情况下都在列名前加上表名,在大型工程中即便列名不同,在未来的操作中也有可能加入相同的列名导致程序报错,故在任何情况下都应加上表名并养成良好习惯。
问题引入:
在左图中显示了薪水(salary)的范围,在右图中显示了员工的薪水,那我们要将员工的薪水与薪水等级(grade_level)对应应该如何操作呢?
此时我们使用非等值连接的方式
代码:
SELECT e.last_name, e.salary, j.grade_level
FROM employees e, job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal;
#上方的where语句里可以看出判断条件是一个范围 即非等值匹配
自连接
当table1和table2本质上是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义。然后两个表再进行内连接,外连接等查询
举例:自己查自己,比如员工资料中有主管的员工号,而主管也是员工,这样就可以通过表自查查询员工所对应的主管和主管信息。
代码举例:
SELECT worker.last_name , manager.last_name
FROM employees worker, employees manager
WHERE worker.manager_id = manager.employee_id ;
内连接: 合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行
外连接: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。
如果是左外连接,则连接条件中左边的表也称为主表
,右边的表称为从表
。
如果是右外连接,则连接条件中右边的表也称为主表
,左边的表称为从表
。
外连接通俗解释:两张表比较起来如果左表数据量更多,则右表一定有缺少的数据项(NULL)此时进行左外连接,则可以把那些NULL的数据项也显示出来。相当于把左右两边的数据量用NULL补充得一样多。
使用JOIN...ON创建连接:
举例:
SELECT table1.column, table2.column,table3.column
FROM table1
JOIN table2
ON table1 和 table2 的连接条件
JOIN table3
ON table2 和 table3 的连接条件
说明
可以使用 ON 子句指定额外的连接条件。
这个连接条件是与其它条件分开的。
ON 子句使语句具有更高的易读性。
关键字 JOIN、INNER JOIN、CROSS JOIN 的含义是一样的,都表示内连接
补充:当你使用WHERE时一般跟在最后一个ON的后面,即把要查的表全部添加完成后使用WHERE,如不这样操作可能会导致程序可以运行但结果错误。
个人理解:JOIN...ON其实还是属于FROM的一部分,根据WHERE紧随FROM的准则,WHRER应在全部表加入后再进行。
内连接的语法
SELECT 字段列表
FROM A表 INNER JOIN B表
ON 关联条件
WHERE 等其他子句;
当JOIN前未加任何限制时默认为内连接。
1.左外连接(LEFT JOIN)
#实现查询结果是A
SELECT 字段列表
FROM A表 LEFT JOIN B表
ON 关联条件
WHERE 等其他子句;
2.右外连接(RIGHT JOIN)
#实现查询结果是B
SELECT 字段列表
FROM A表 RIGHT JOIN B表
ON 关联条件
WHERE 等其他子句;
3.满外连接(FULL OUTER JOIN)
满外连接的结果 = 左右表匹配的数据 + 左表没有匹配到的数据 + 右表没有匹配到的数据。
SQL99是支持满外连接的。使用FULL JOIN 或 FULL OUTER JOIN来实现。
需要注意的是,MySQL不支持FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT join代替。
利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION或UNION ALL关键字分隔。
SELECT column,... FROM table1
UNION [ALL]
SELECT column,... FROM table2
UNION返回结果集的并集,去除重复记录
UNION ALL 返回结果集的并集,不去除重复记录(两个集合的共有部分叠加了两次)
注意:执行UNION ALL语句时所需要的资源比UNION语句少。如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL语句,以提高数据查询的效率。
由于我们可以用 JOIN 实现去重,所以推荐使用UNION ALL。
中图:内连接
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`;
解释:正常的取交集连接,无理解困难。
左上图:左外连接
#左上图:左外连接
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`;
解释:想要显示出完整左表,此时使用左外连接,右表缺失的部分用NULL补足。
可以看到右表缺省的部位用NULL补齐了
右上图:右外连接
#右上图:右外连接
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;
解释:与左上一致。
左中图:
#左中图:A - A∩B
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
解释:此刻要求左表中没有与右表有共同值的数据项,先看上面我左外连接的解释,此时左外连接中左表对应右表的NULL,那么加上`WHERE d.`department_id` IS NULL`这个条件语句就可以把左表对应右表NULL的数据项全部筛选出来。
右中图
#右中图:B-A∩B
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL
左下:
#左下图:满外连接
# 左中图 + 右上图 A∪B
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL #没有去重操作,效率高
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;
解释,看原图得知,左中图加右上图拼接的话不会有重复数据,所以直接使用UNION ALL。这就是我们自己去重的方法
右下图
#右下图
#左中图 + 右中图 A ∪B- A∩B 或者 (A - A∩B) ∪ (B - A∩B)
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL
把两张表互相存在不对应的项全部表示出来了。
最后我们要注意:
我们要控制连接表的数量
。多表连接就相当于嵌套 for 循环一样,非常消耗资源,会让 SQL 查询性能下降得很严重,因此不要连接不必要的表。在许多 DBMS 中,也都会有最大连接表的限制。