SELECT last_name, department_name FROM employees, departments; # 2889rows
1 2 3
SELECTCOUNT(last_name) FROM employees; -- 107 rows SELECTCOUNT(department_name) FROM departments; -- 27 rows SELECT107*27; -- 2889
即将两个表的所有行进行了组合,将所有非重复组合一同输出。
1.2 笛卡尔积
定义:对于集合 A,B 称由 A,B 则称 A 和 B 的笛卡尔积是 A 和 B 的所有可能组合。
A×B={(a,b)∣a∈A,b∈B}
则容易得知新的集合元素个数为 ∣A∣×∣B∣ ,其中 ∣⋅∣ 表示元素个数。
1.3 CROSS JOIN 交叉连接
笛卡尔积也称为交叉连接,可以使用 CROSS JOININNER JOINJOIN链接二表。
1 2 3 4 5 6 7 8 9 10 11
SELECT last_name, department_name FROM employees, departments;
SELECT last_name, department_name FROM employees CROSSJOIN departments;
SELECT last_name, department_name FROM employees INNERJOIN departments;
SELECT last_name, department_name FROM employees JOIN departments;
不通过 WHERE 或 ON 添加条件,则会出现笛卡尔积错误
1.4 WHERE 加入链接条件
为了避免笛卡尔积, 可以在WHERE 加入有效的连接条件。格式为
1 2 3
SELECT 表1.列名, 表1.列名 FROM 表1, 表2 WHERE 表1.列名1= 表2.列名2; -- 有效的连接条件
例:根据部门 id 筛选
1 2 3 4
# WHERE 加入条件 SELECT employees.last_name, departments.department_name, employees.department_id FROM employees, departments WHERE employees.department_id = departments.department_id;
-- 根据 job_id 外键连接 jobs 表的 job_title SELECT employees.last_name, jobs.job_title, employees.job_id FROM employees, jobs WHERE employees.job_id = jobs.job_id;
多个条件 AND 连接
1 2 3
SELECT employees.last_name, departments.department_name, employees.department_id FROM employees, departments WHERE employees.department_id = departments.department_id AND departments.department_name ='Shipping';
多个表中有相同列时,必须在列名之前加上表名前缀
【推荐使用,方便标注各表各列,提高查询效率】
使用别名可以简化查询
1 2 3
SELECT e.last_name, d.department_name, e.department_id FROM employees AS e, departments AS d WHERE e.department_id = d.department_id;
一但使用了别名,这一个语句中必须使用别名替代
连接多个表
例如:展示员工 last_name, department_name, city
1 2 3 4 5 6
SELECT e.last_name, d.department_name, lc.city FROM employees AS e, departments as d, locations as lc WHERE e.department_id = d.department_id AND d.location_id = lc.location_id;
连接 N 个表,至少需要 N-1 个条件
2.1.3 非等值连接
1 2 3 4 5
SELECT e.last_name, e.salary, j.grade_level AS GRADE FROM employees AS e, job_grades AS j WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal -- 按工资分档 ORDERBY salary DESC; -- 按照工资排序
2.2 自连接 & 非自连接
使用别名的方式把 1 张表虚拟成 2 张表,而后两表可以进行内连接和外连接
1 2 3 4 5
SELECT CONCAT(worker.last_name, ' works for ' , manager.last_name) AS relationship -- 连接为长字符串 FROM employees AS worker, employees AS manager WHERE worker.manager_id = manager.employee_id;
将 employees 表虚拟为 表 worker 和 manager,然后自连接
2.3 内连接 & 外连接
内连接:合并的表存在联系,即存在相同列。结果中不包含一个表与另一个表不匹配的行
内连接使用 INNER JOIN
外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回不满足条件的行
外连接使用 LEFT JOINRIGHT JOIN,特别地,对 MySQL 而言全外连接需要使用 UNION 方法
不满足条件的行 —— 相应的列为空 NULL
当返回不满足条件的行来自左表时,则为左外连接,左边的表也称为主表 ,右边的表称为从表
当返回不满足条件的行来自右表时,则为右外连接,右边的表也称为主表 ,左边的表称为从表
3 多表查询代码
使用 SQL99 语法实现
3.1 基础语法 JOIN ON
标准格式
1 2 3 4 5 6 7 8
SELECT table1.column, table2.column, table3.column FROM table1 JOIN table2 ON table1 和 table2 的连接条件 JOIN table3 ON table2 和 table3 的连接条件
各个条件相互独立
使用 JOINCROSS JOININNER JOIN 含义相同,均代表内连接
下面看具体内连接案例:
3.2 内连接
1 2 3 4 5 6
SELECT table1.column, table2.column, FROM table1 JOIN table2 ON 条件 WHERE 其他;
例:查询 last_name, department_name, city 员工为 'Chen'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
SELECT e.last_name, d.department_name, lc.city FROM employees e JOIN departments d ON e.department_id = d.department_id JOIN locations lc ON d.location_id = lc.location_id; WHERE e.last_name ='Chen';
SELECT d.department_name, e.last_name, e.department_id FROM employees e RIGHTOUTERJOIN departments d ON e.department_id = d.department_id;
3.3.3 满外连接 FULL JOIN ON
1
满外连接的结果 = 左右表匹配到的数据 + 左表没有匹配到的数据 + 右表没有匹配到的数据
标准语法
1 2 3 4 5 6
-- 互相charity SELECT 字段列表 FROM A表 FULLJOIN B表 ON 关联条件 WHERE 其他;
注:MySQL 不支持 FULL JOIN ,但可以通过合并左外连接和右外连接的方式实现。
可以简单理解:FULL JOIN <=> LEFT JOIN UNION RIGHT JOIN
4 合并查询结果 UNION
UNION 可以将多个 SELECT 语句得到的结果合并成一个表输出:
匹配:多个 SELECT 语句得到的结果集列数和数据类型必须相同
去重与否:使用 UNION 会去除重复数据;使用 UNION ALL 不会去除重复数据
1 2 3
SELECTcolumn, ... FROM table1 UNION [ALL] -- UNION 或者 UNION ALL SELECTcolumn, ... FROM table2
例:查询部门编号 department_id > 90 或 邮箱 email 包含 a 的员工信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14
-- 法一:使用 WHERE SELECT last_name, email, department_id FROM employees WHERE email LIKE'%a%' OR department_id >90;
-- 法二:使用 UNION SELECT last_name, email, department_id FROM employees WHERE email LIKE'%a%' UNION SELECT last_name, email, department_id FROM employees WHERE department_id >90;
5 JOIN 实现的几种关系(总结)
5.1 内连接:A 交 B
A 表与 B 表内连接,相当于 A∩B
1 2 3 4
SELECT A.column, B.column FROM A JOIN B ON 条件;
5.2 左外连接:A 交 (A 交 B)
A 表作为主表,B 表作为从表插入 A ,相当于 A+A∩B
1 2 3 4
SELECT A.column, B.column FROM A LEFTJOIN B ON 条件;
5.3 右外连接:B 交 (A 交 B)
B 表作为主表,A 表作为从表插入 B ,相当于 B+A∩B
1 2 3 4
SELECT A.column, B.column FROM A RIGHTJOIN B ON 条件;
左外连接和右外连接本质相同
5.4 WHERE 找出空值 IS NULL:只属于 A
A 表作为主表,B 表插入,但去除完美匹配的,即 A 表中与 B 表无关的部分。相当于 A−A∩B
1 2 3 4 5
SELECT A.column, B.column FROM A RIGHTJOIN B ON 条件 WHERE B.column ISNULL;
例:查询 部门表中无员工的部门
1 2 3 4 5
SELECT d.department_name, d.department_id, e.department_id FROM departments d LEFTJOIN employees e ON d.department_id = e.department_id WHERE e.department_id ISNULL
5.5 UNION 合并:A 并 B
UNION 可以将多个 SELECT 语句得到的结果合并成一个表输出,相当于 A∪B
1 2 3 4 5 6 7 8 9 10
SELECTcolumn, ... FROM A LEFTJOIN B ON A.col = B.col WHERE B.col ISNULL UNIONALL SELECTcolumn, ... FROM A RIGHTJOIN B ON A.col = B.col
-- 法一:利用 WHERE d.department_id IS NULL 手动去重 SELECT employee_id, last_name, department_name FROM employees e LEFTJOIN departments d ON e.department_id = d.department_id WHERE d.department_id ISNULL UNIONALL SELECT employee_id, last_name, department_name FROM employees e RIGHTJOIN departments d ON e.department_id = d.department_id;
-- 法二:直接使用 UNION 自动去重 SELECT employee_id, last_name, department_name FROM employees e LEFTJOIN departments d ON e.department_id = d.department_id UNION SELECT employee_id, last_name, department_name FROM employees e RIGHTJOIN departments d ON e.department_id = d.department_id;
5.6 找出空值后 UNION 合并:对称差
查询所有匹配失败的行数据,相当于 A∪B−A∩B
1 2 3 4 5 6 7 8 9 10 11
SELECTcolumn, ... FROM A LEFTJOIN B ON A.col = B.col WHERE B.col ISNULL UNIONALL SELECTcolumn, ... FROM A RIGHTJOIN B ON A.col = B.col WHERE A.col ISNULL-- 全部加上 IS NULL 判断
例:查询所有失败行数据
1 2 3 4 5 6 7 8 9 10 11
SELECT employee_id, last_name, department_name FROM employees e LEFTJOIN departments d ON e.department_id = d.department_id WHERE d.department_id ISNULL UNIONALL SELECT employee_id, last_name, department_name FROM employees e RIGHTJOIN departments d ON e.department_id = d.department_id WHERE e.department_id ISNULL;
6 特殊等值连接
6.1 自然连接
NATURAL JOIN 会自动查询两张表中所有相同的字段,然后进行等值连接
1 2 3 4
-- 这是 SQL99 新语法 SELECT employee_id, last_name, department_name FROM employees e NATURALJOIN departments d;
-- 上面自然连接等价于 SELECT employee_id, last_name, department_name FROM employees e JOIN departments d ON e.department_id = d.department_id AND e.manager_id = d.manager_id; -- 是 AND 而不是 OR
6.2 USING 连接
当有多个同名字段时,USING 可以指定同名字段进行连接
1 2 3 4 5 6 7 8 9 10
SELECT employee_id, last_name, department_name FROM employees e, departments d WHERE e.department_id = d.department_id; -- 利用 USING 简化
-- 可以简化为 SELECT employee_id, last_name, department_name FROM employees e JOIN departments d USING (department_id); -- 指定 department_id
6.3 总结:等值连接的三种写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
-- 法一:WHERE 限制条件 SELECT employee_id, last_name, department_name FROM employees e, departments d WHERE e.department_id = d.department_id;
-- 法二:JOIN ... ON + 条件 SELECT employee_id, last_name, department_name FROM employees e JOIN departments d ON e.department_id = d.department_id; -- 法三:USING 指定同名字段 SELECT employee_id, last_name, department_name FROM employees e JOIN departments d USING (department_id); -- 指定 department_id