6.1 一个案例引入多表查询
现在有两张表employees和department。
现在的要求是- # 查询每个员工的姓名和对应的部门名称
- SELECT last_name, department_name
- FROM employees, departments;
- # 部分结果举例
- /*
- | Gietz | Payroll |
- | Gietz | Recruiting |
- | Gietz | Retail Sales |
- | Gietz | Government Sales |
- | Gietz | IT Helpdesk |
- | Gietz | NOC |
- | Gietz | IT Support |
- | Gietz | Operations |
- | Gietz | Contracting |
- | Gietz | Construction |
- | Gietz | Manufacturing |
- | Gietz | Benefits |
- | Gietz | Shareholder Services |
- | Gietz | Control And Credit |
- | Gietz | Corporate Tax |
- | Gietz | Treasury |
- | Gietz | Accounting |
- | Gietz | Finance |
- | Gietz | Executive |
- | Gietz | Sales |
- | Gietz | Public Relations |
- | Gietz | IT |
- | Gietz | Shipping |
- | Gietz | Human Resources |
- | Gietz | Purchasing |
- | Gietz | Marketing |
- | Gietz | Administration |
- +-------------+----------------------+
- 2889 rows in set (1.04 sec)
- */
复制代码 从结果上看最终查询的数量是 employees表的行数 * departments表的行数 (107 * 27 )。
那么为什么会出现这种现象呢?
笛卡尔积
这里需要引入笛卡尔积的概念,什么是笛卡尔积呢?
从图中可以看出,笛卡尔积就是所有可能性结果,也称之为交叉连接,英文是CROSS JOIN,SQL99中也是使用CROSS JOIN来表示交叉连接,作用就是可以将任意表进行连接,哪怕两张表不相关。
笛卡尔积错误的产生
- 省略多个表的连接条件
- 连接条件无效
- 所有表中的所有行互相连接
为了避免笛卡尔积的条件,需要用WHERE加入有效的连接条件
- # 查询每个员工的姓名和对应的部门名称
- SELECT last_name, department_name
- FROM employees, departments
- WHERE employees.department_id = departments.department_id;
复制代码 6.2 多表查询的分类
等值连接 VS 非等值连接
等值连接
利用一张表的某列的值和另外一张表中某列的值相等的关系,把多余的数据过滤掉
刚才的查询就是一个典型的等值连接- # 查询每个员工的姓名和对应的部门名称
- SELECT last_name, department_name
- FROM employees, departments
- WHERE employees.department_id = departments.department_id;
复制代码
- 多个条件可以用逻辑运算符连接起来
- 如果两个表中有相同的列名,这个时候需要带上表名.区分,可以给 表取名然后用别名区分
注:如果给表起了别名,过了条件中只能使用别名,不能使用原来的表名
- 多个表连接需要更多的条件,N个表连接至少愮用N-1个条件
非等值连接
除了利用相等,还可以利用范围条件实现两个表的查询。
- # 查询所有员工的工资等级
- 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;
复制代码 自连接 VS 非自连接
- table1和table2本质上是同一张表,只是取别名的方式虚拟成两张表代表不同的意义,然后两张表再进行内连接,外连接等查询,这就是自连接。
- # 查询employees表,返回“Xxx works for Xxx”
- SELECT CONCAT(worker.last_name," works for ",manager.last_name)
- FROM employees worker, employees manager
- WHERE worker.manager_id = manager.employee_id;
- # 查询出last_name为 ‘Chen’ 的员工的 manager 的信息
- SELECT manager.*
- FROM employees worker, employees manager
- WHERE worker.last_name = 'Chen' AND worker.manager_id = manager.employee_id;
复制代码 内连接 VS 外连接
刚才上面的查询都是内连接
- 内连接:合并具有同一列的两个以上的表的行,结果集中不包含一个表和另一个表不匹配的行
- 外连接:两个个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行,这种连接称为左(或右)外连接,没有匹配的行,结果表中相对应的列为NULL
- 如果是左外连接,则连接条件中的左边的表为主表,右边的表为从表
如果是左外连接,则连接条件中的右边的表为主表,左边的表为从表
6.3 多表查询的实现
基本语法
- # 使用JOIN ON子句创建连接
- SELECT table1.column, table2.column,table3.column
- FROM table1
- JOIN table2 ON table1 和 table2 的连接条件
- JOIN table3 ON table2 和 table3 的连接条件
复制代码 内连接(INNER JOIN)的实现
- 语法
- SELECT 字段列表
- FROM A表 INNER JOIN B表
- ON 关联条件
- WHERE 等其他子句;
复制代码 - 练习
继续用刚才的等值连接的题目- # 查询每个员工的姓名和对应的部门名称
- SELECT last_name, department_name
- FROM employees INNER JOIN departments
- ON employees.department_id = departments.department_id;
- # INNER JOIN可以省略成JOIN
- # 查询员工工号以及对应的工作城市和工作部门
- SELECT employee_id, city, deparment_name
- FROM employees e JOIN departments d
- ON e.department_id = d.department_id
- JOIN ON locations l
- ON d.location_id = l.location_id;
复制代码
外连接(OUTER JOIN)的实现
- 语法
- # 左外连接,A表是主表,最终结果包含A表所有内容
- SELECT 字段列表
- FROM A表 LEFT OUTER JOIN B表
- ON 关联条件
- WHERE 等其他子句;
- # 右外连接,B表是主表,最终结果包含B表所有内容
- SELECT 字段列表
- FROM A表 RIGHT OUTER JOIN B表
- ON 关联条件
- WHERE 等其他子句;
复制代码和刚才一样,这里的OUTER可以省略
- 举例
- # 左外连接
- # 查询所有员工对应的部门和部门名称
- SELECT e.last_name, e.employee_id, d.department_id, department_name
- FROM employees e LEFT JOIN departments d
- ON e.department_id = d.department_id;
- /*
- 有一条数据是没有部门的但是还会在查询结果中
- | Grant | 178 | NULL | NULL |
- */
- # 右外连接
- # 查询部门对应的员工
- SELECT e.last_name, e.employee_id, d.department_id, department_name
- FROM employees e RIGHT JOIN departments d
- ON e.department_id = d.department_id;
- /*
- 有几个部门没有员工也会在查询结果中
- | NULL | NULL | 120 | Treasury |
- | NULL | NULL | 130 | Corporate Tax |
- | NULL | NULL | 140 | Control And Credit |
- | NULL | NULL | 150 | Shareholder Services |
- | NULL | NULL | 160 | Benefits |
- | NULL | NULL | 170 | Manufacturing |
- | NULL | NULL | 180 | Construction |
- | NULL | NULL | 190 | Contracting |
- | NULL | NULL | 200 | Operations |
- | NULL | NULL | 210 | IT Support |
- | NULL | NULL | 220 | NOC |
- | NULL | NULL | 230 | IT Helpdesk |
- | NULL | NULL | 240 | Government Sales |
- | NULL | NULL | 250 | Retail Sales |
- | NULL | NULL | 260 | Recruiting |
- | NULL | NULL | 270 | Payroll |
- */
复制代码 - 满外连接
- FULL OUTER JOIN 就是满外连接,所有的查询结果都会在出现,无论左边右边的表是否有对应结果
- MYSQL不支持FULL JOIN,采用 UNION替代
UNION
UNION有两种查询,分别是UNION和UNION ALL
- UNION操作
返回两个结果集的并集,并且去掉重复记录
- UNION ALL操作
返回两个结果集的并集,不去掉重复记录
UNION ALL所需要的资源比UNION少,如果确定结果没有重复记录,建议使用UNION ALL,提高数据查询速度。
练习- # 查询部门编号>90或邮箱包含a的员工信息
- # 直接查询
- SELECT * FROM employees e
- WHERE e.department_id > 90 OR e.email LIKE "%a%";
- # UNION查询
- SELECT * FROM employees e1
- WHERE e1.email LIKE "%a%"
- UNION
- SELECT * FROM employees e2
- WHERE e2.department_id > 90;
- # 查询中国用户中男性的信息以及美国用户中年男性的用户信息
- # 表t_chinamale 和 表 t_usmale
- SELECT id,cname FROM t_chinamale WHERE csex='男'
- UNION ALL
- SELECT id,tname FROM t_usmale WHERE tGender='male';
复制代码 6.4 7种SQL JOIN的实现
- # 还是拿员工表和部门表作为例子
- # 左上角 左外连接
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e LEFT JOIN departments d
- ON e.department_id = d.department_id;
- # 右上角 右外连接
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e RIGHT JOIN departments d
- ON e.department_id = d.department_id;
- # 中间图内连接
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e JOIN departments d
- ON e.department_id = d.department_id;
- # 左下图 这个就是两个集合的并集用UNION就可以处理
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e LEFT JOIN departments d
- ON e.department_id = d.department_id
- UNION
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e RIGHT JOIN departments d
- ON e.department_id = d.department_id;
复制代码 其余四个需要一定的解释,但是拿出来说
先说左中图和右中图,两个是一致的只是最终的结果是相反的
现在以左中图作为例子,实质上结果是 A - A∩B,- # 左中图 A - A∩B
- # 可以先查询出A的外连接
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e LEFT JOIN departments d
- ON e.department_id = d.department_id
- WHERE d.department_id IS NULL; # 在这里需要删掉B为NULL的时候
复制代码这里有的同学可能会误解为什么是判断B表的字段是不是NULL为什么不是A表的?
这里解释一下,JOIN之后的操作实质上就是从笛卡尔集中取出数据,那么在查询出A表的外连接的时候,表会变成什么样呢,可以一起来看看
可能有人会说,这里A表B表的字段都是NULL,为什么不判断B表的呢?
在这里我想说的是,这里尚硅谷给的例子比较特殊,用的是外键去判断,如果这里的条件不是用等值的外键去判断呢,假设A没有这个字段,是不是只有B才有这个字段,也就是说当前获取的集合里面只能通过B表去判断是否为NULL!!!
同理- # 右中图 B - A∩B
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e RIGHT JOIN departments d
- ON e.department_id = d.department_id
- WHERE e.department_id IS NULL; # 在这里需要删掉B为NULL的时候
复制代码 剩下的左下图就是两个的并集- SELECT e.employee_id, e.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
- SELECT e.employee_id, e.last_name, department_name
- FROM employees e RIGHT JOIN departments d
- ON e.department_id = d.department_id
- WHERE e.department_id IS NULL;
复制代码 6.5 SQL99的新特性
自然连接
自然连接就是NATURAL JOIN ,自然连接会自动匹配两张表的相同字段,然后进行等值连接- # 正常内连接写法
- 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`;
- # 使用自然连接
- SELECT employee_id,last_name,department_name
- FROM employees e NATURAL JOIN departments d;
复制代码 USING连接
USING连接和自然连接很接近,自然连接时自动匹配所有相同字段,USING是指定一个相同字段- # 内连接
- 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);
复制代码注:可以看出来内连接和刚才的等值连接很像,只是所使用的语法不同,其实从实质上两个可以理解成相同的,在SQL92规范中,内连接就是等值连接,而外连接只需要用+,但是MySQL不支持这种写法,现在的SQL99虽然写起来繁琐,但是比较好分辨。- #左外连接
- SELECT last_name,department_name
- FROM employees ,departments
- WHERE employees.department_id = departments.department_id(+);
- #右外连接
- SELECT last_name,department_name
- FROM employees ,departments
- WHERE employees.department_id(+) = departments.department_id;
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |