第九章:子查询

第九章:子查询

9.1:子查询的基本使用

  1. 子查询的基本语法结构

    SELECT ....
    FROM ....
    WHERE expr operator (SELECT ...
                         FROM ...
                         WHERE ...);
    
    • 子查询(内查询)在主查询之前一次执行完成。
    • 子查询的结果被主查询(外查询)使用。
    • 注意事项
      1. 子查询要包含在括号内。
      2. 将子查询放在比较条件的右侧。
      3. 单行操作符对应单行子查询,多行操作符对应多行子查询
  2. 子查询的分类

    • 分类方式1

      我们按内查询的结果返回一条还是多条记录,将子查询分为单行子查询多行子查询

    • 分类方式2

      ​ 我们按内查询是否被执行多次,将子查询划分为相关(或关联)子查询不相关(或非关联)子查询

      ​ 子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询的条件进行执行,那么这样的子查询叫做不相关子查询。

      ​ 同样,如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部,这种嵌套的执行方式就称为相关子查询。

9.2:单行子查询

  1. 单行比较操作符

    操作符 含义
    = equal to
    > greater than
    >= greater than or equal to
    < less than
    <= less than or equal to
    <> not equal to
  2. 代码示例

    • 返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资。

      SELECT last_name, job_id,salary
      FROM employees
      WHERE job_id = (
      		SELECT job_id
      		FROM employees
      		WHERE employee_id = 141
      		)
      AND salary > (
      	      SELECT salary 
      	      FROM employees
      	      WHERE employee_id = 143
      	      );
      
    • 返回公司工资最少的员工的last_namejob_idsalary

      SELECT last_name, job_id, salary
      FROM employees
      WHERE salary = (
      		SELECT MIN(salary)
      		FROM employees
      		);
      
  3. HAVING中的子查询

    首先执行子查询,向主查询中的HAVING子句返回结果。

    • 查询最低工资大于110号部门最低工资的部门id和其最低工资

      SELECT department_id, MIN(salary)
      FROM employees
      WHERE department_id IS NOT NULL
      GROUP BY department_id
      HAVING MIN(salary) > (
      		      SELECT MIN(salary)
      		      FROM employees
      		      WHERE department_id = 110
      		      );
      
  4. CASE中的子查询

    • 显示员工的employee_idlast_namelocation。其中,若员工department_idlocation_id为1800的department_id相同,则locationCanada,其余则为USA

      SELECT employee_id, last_name, CASE department_id 
      								WHEN (
          								   SELECT department_id 
                                                FROM departments 
                                                WHERE location_id = 1800) THEN 'Canada'
      								ELSE 'USA' END "location"
      FROM employees;
      
  5. 子查询中的空值问题

    SELECT last_name, job_id
    FROM employees
    WHERE job_id = (
        	# 子查询为NULL
    		SELECT job_id
    		FROM employees
    		WHERE last_name = 'Haas'
    		);
    
  6. 非法使用子查询语句

    # 错误代码:1242
    # Subquery returns more than 1 row
    SELECT employee_id, last_name
    FROM employees
    WHERE salary = (
    		SELECT MIN(salary)
    		FROM employees
    		GROUP BY department_id
    		);
    

9.3:多行子查询

  1. 多行比较操作符

    操作符 含义
    IN 等于列表中的任意一个
    ANY 需要和单行比较操作符一起使用,和子查询返回的某一个值比较
    ALL 需要和单行比较操作符一起使用,和子查询返回的所有值比较
    SOME 实际上是ANY的别名,作用相同,一般常使用ANY
  2. 实例代码

    • 返回其他job_id中比job_idIT_PROG部门任一工资低的员工的员工号、姓名、job_id以及salary

      SELECT employee_id, last_name, job_id, salary
      FROM employees
      WHERE job_id <> 'IT_PROG'
      AND salary < ANY (
      		  SELECT salary
      		  FROM employees
      		  WHERE job_id = 'IT_PROG'
      		  );
      
    • 返回其他job_id中比job_idIT_PROG部门所有工资都低的员工的员工、姓名、job_id以及salary

      SELECT employee_id, last_name, job_id, salary
      FROM employees
      WHERE job_id <> 'IT_PROG'
      AND salary < ALL (
      		  SELECT salary
      		  FROM employees
      		  WHERE job_id = 'IT_PROG'
      		  );
      
    • 查询平均工资最低的部门id

      # 方式一
      SELECT department_id
      FROM employees
      GROUP BY department_id
      HAVING AVG(salary) = (
      		SELECT MIN(avg_sal)
      		FROM (
      			SELECT AVG(salary) avg_sal
      			FROM employees
      			GROUP BY department_id
      			) t_dept_avg_sal
      		);
      		
      # 方式二
      SELECT department_id
      FROM employees
      GROUP BY department_id
      HAVING AVG(salary) <= ALL(
      		SELECT AVG(salary) avg_sal
      		FROM employees
      		GROUP BY department_id
      		);
      
  3. 空值问题

    • 一行数据都没有

      SELECT last_name
      FROM employees
      WHERE employee_id NOT IN(
      		SELECT manager_id
      		FROM employees
      		);
      

9.4:相关子查询

  1. 相关子查询执行流程

    ​ 如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为关联子查询

    ​ 相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。
    第九章:子查询_第1张图片

  2. 代码示例

    • 查询员工中工资大于本部门平均工资的员工的last_namesalary和其department_id

      # 方式一: 相关子查询
      SELECT last_name, salary, department_id
      FROM employees e1
      WHERE salary > (
      		SELECT AVG(salary)
      		FROM employees e2
      		WHERE department_id = e1.`department_id`
      		);
      		
      # 方式二: 在FROM中使用子查询
      SELECT e.last_name, e.salary, e.department_id
      FROM employees e, (
      		SELECT department_id, AVG(salary) avg_sal
      		FROM employees
      		GROUP BY department_id) t_dept_avg_sal
      WHERE e.`department_id` = t_dept_avg_sal.department_id
      AND e.salary > t_dept_avg_sal.avg_sal;
      
    • 查询员工的idsalary,按照department_name排序。

      # 在ORDER  BY中使用子查询
      SELECT employee_id, salary
      FROM employees e
      ORDER BY (
      	SELECT department_name
      	FROM departments d
      	WHERE e.`department_id` = d.`department_id`
      	) ASC;
      
  3. EXISTSNOT EXISTS关键字

    关联子查询通常也会和EXISTS操作符一起来使用,用来检查在子查询中是否存在满足条件的行。

    • 如果在子查询中不存在满足条件的行:条件返回FALSE、继续在子查询中查找
    • 如果在子查询中满足条件的行:不在子查询中继续查找、条件返回TRUE
    • NOT EXISTS关键字表示如果不存在某种条件,则返回TRUE,否则返回FALSE

    查询公司管理者的employee_idlast_namejob_iddepartment_id信息。

    #方式1:自连接
    SELECT DISTINCT mgr.employee_id, mgr.last_name, mgr.job_id, mgr.department_id
    FROM employees emp JOIN employees mgr
    ON emp.manager_id = mgr.`employee_id`;
    
    #方式2:子查询
    SELECT employee_id, last_name, job_id, department_id
    FROM employees
    WHERE employee_id IN (
    		SELECT DISTINCT manager_id
    		FROM employees
    		);
    
    #方式3:使用EXISTS
    SELECT employee_id, last_name, job_id, department_id
    FROM employees e1
    WHERE EXISTS (
    		SELECT * 
    		FROM employees e2
    		WHERE e1.`employee_id` = e2.`manager_id`
    		);
    

    查询departments表中,不存在于employees表中的部门的department_iddepartment_name

    #方式1:
    SELECT d.department_id, d.department_name
    FROM employees e RIGHT JOIN departments d
    ON e.`department_id` = d.`department_id`
    WHERE e.`department_id` IS NULL;
    
    #方式2:
    SELECT department_id, department_name
    FROM departments d
    WHERE NOT EXISTS (
    		SELECT *
    		FROM employees e
    		WHERE d.`department_id` = e.`department_id`
    		);
    
  4. 一个思考题

    • 自连接和子查询两种方式有好坏之分吗?

      ​ 自连接方式好。

      ​ 题目中可以使用子查询,也可以使用自连接。一般情况建议你使用自连接,因为在许多BDMS的处理过程中,对于自连接的处理速度要比子查询快得多。

      ​ 可以这样理解:子查询实际上是通过未知表进行查询后的条件判断,而自连接是通过已知的自身数据表进行条件判断,因此在大部分DBMS中都对自连接处理进行了优化。

你可能感兴趣的:(MySQL,数据库,sql,mysql)