牛客SQL.4

147 阅读3分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

描述

有一个员工表,employees简况如下:

img

有一个部门表,dept_emp简况如下:

img

请你查找所有已经分配部门的员工的last_name和first_name以及dept_no,未分配的部门的员工不显示,以上例子如下:

img

示例1

drop table if exists  `dept_emp` ; 
drop table if exists  `employees` ; 
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
INSERT INTO dept_emp VALUES(10001,'d001','1986-06-26','9999-01-01');
INSERT INTO dept_emp VALUES(10002,'d002','1996-08-03','9999-01-01');
INSERT INTO employees VALUES(10001,'1953-09-02','Georgi','Facello','M','1986-06-26');
INSERT INTO employees VALUES(10002,'1964-06-02','Bezalel','Simmel','F','1985-11-21');
INSERT INTO employees VALUES(10003,'1959-12-03','Parto','Bamford','M','1986-08-28');
INSERT INTO employees VALUES(10004,'1954-05-01','Chirstian','Koblick','M','1986-12-01');
​
输出
Facello|Georgi|d001
Simmel|Bezalel|d002

方法一

根据题目中的未分配的部门的员工不显示可知,应该用的是内连接;且在内连接和外连接都都可用的情况下,最好也是选用内连接,这样MYSQL内部会为我们选择最佳方案;

dept_emp表中存在的员工,说明已经分配的部门,根据员工emp_no再去employees表中查询详细信息;

SELECT a.last_name, a.first_name, b.dept_no 
FROM employees a, dept_emp b
WHERE a.emp_no=b.emp_no

运行效果

image-20211105151530850

b为驱动表来查询,对b表的查询方式有主键常量比较和全表扫描,由于a.emp_no=b.emp_no对于驱动表来说,等式的另一端是未知的,所以无法使用常量比较;因此选择了全表扫描;对于被驱动表来说,访问方式如下:

image-20211105152135432

有常量比较和全表扫描两种,从cost字段可以看成,主键的常量比较开销更小,所以选择了第一种;最后b表的1.4加a表的2.4,总共开销就是3.8;

再来看看以a表为驱动表的开销:

image-20211105152618006

image-20211105152633137

总开销为6.6,所以最终的选择是以b表为驱动表;

在某种情况下,外连接是可以优化成内连接的,例如还是此题为例,我们写个外连接:

SELECT a.last_name, a.first_name, b.dept_no 
FROM employees a LEFT JOIN dept_emp b
ON a.emp_no=b.emp_no
WHERE b.emp_no IS NOT NULL;

由于未分配的员工不显示,所以我们加上WHERE b.emp_no IS NOT NULL条件;

image-20211105154611399

通过上图我们可以看到,在准备阶段,我们输入的语句最后被优化成了下面的语句

SELECT a.last_name, a.first_name, b.dept_no 
FROM employees a JOIN dept_emp b
WHERE b.emp_no IS NOT NULL AND a.emp_no=b.emp_no

可见,我们的左外连接,在内部被优化成了内连接;接着在根据内连接的步骤,判断如何执行开销最低,来达到一个最优执行方案;可以看到最后优化后,开销为2.6;

image-20211105155007963

通过这次例子,我们知道,当外连接时,我们指定被驱动表不能为空时(可以显式指定,也可以隐式指定),是等价于内连接的;