zoukankan      html  css  js  c++  java
  • 十二、子查询(非常重要,高手必备)

    一、子查询

      出现在select语句中的select语句,称为子查询或内查询。

      外部的select查询语句,称为主查询或外查询。

    二、子查询分类

      按结果集的行列数不同分为4种

    • 标量子查询(结果集只有一行一列)

    • 列子查询(结果集只有一列多行)

    • 行子查询(结果集有一行多列)

    • 表子查询(结果集一般为多行多列)

      按子查询出现在主查询中的不同位置分

    • select后面:仅仅支持标量子查询。

    • from后面:支持表子查询。

    • where或having后面:支持标量子查询(单列单行)、列子查询(单列多行)、行子查询(多列多行)

    • exists后面(即相关子查询):表子查询(多行、多列)

    三、select后面的子查询

      子查询位于select后面的,仅仅支持标量子查询

      查询每个部门员工个数:

      SELECT
        a.*,
        (SELECT count(*)
         FROM employees b
         WHERE b.department_id = a.department_id) AS 员工个数
      FROM departments a;

      查询员工号=102的部门名称:

      SELECT (SELECT a.department_name
                FROM departments a, employees b
                WHERE a.department_id = b.department_id
                       AND b.employee_id = 102) AS 部门名;

    四、from后面的子查询  

      将子查询的结果集充当一张表,要求必须起别名,否者这个表找不到。

      然后将真实的表和子查询结果表进行连接查询。

      查询每个部门平均工资的工资等级:

      SELECT
        t1.department_id,
        sa AS '平均工资',
        t2.grade_level
      FROM (SELECT
                department_id,
                avg(a.salary) sa
              FROM employees a
              GROUP BY a.department_id) t1, job_grades t2
      WHERE
        t1.sa BETWEEN t2.lowest_sal AND t2.highest_sal;

    五、where和having后面的子查询  

      where或having后面,可以使用

    1. 标量子查询(单行单列行子查询)

    2. 列子查询(单列多行子查询)

    3. 行子查询(多行多列) 

      特点

    1. 子查询放在小括号内。

    2. 子查询一般放在条件的右侧。

    3. 标量子查询,一般搭配着单行操作符使用,多行操作符   >、<、>=、<=、=、<>、!=

    4. 列子查询,一般搭配着多行操作符使用    

        in(not in):列表中的“任意一个”

        any或者some:和子查询返回的“某一个值”比较,比如a>som(10,20,30),a大于子查询中任意一个即可,a大于子查询中最小值即可,等同于a>min(10,20,30)。

        all:和子查询返回的“所有值”比较,比如a>all(10,20,30),a大于子查询中所有值,换句话说,a大于子查询中最大值即可满足查询条件,等同于a>max(10,20,30);

       5.子查询的执行优先于主查询执行,因为主查询的条件用到了子查询的结果。

        mysql中的in、any、some、all

        in,any,some,all分别是子查询关键词之一。

        in:in常用于where表达式中,其作用是查询某个范围内的数据

        any和some一样: 可以与=、>、>=、<、<=、<>结合起来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的任何一个数据。

        all:可以与=、>、>=、<、<=、<>结合是来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的其中的所有数据。

        下文中会经常用到这些关键字。  

      标量子查询 

        一般标量子查询,示例:查询谁的工资比Abel的高?

        /*①查询abel的工资【改查询是标量子查询】*/
        SELECT salary
        FROM employees
        WHERE last_name = 'Abel';

        /*②查询员工信息,满足salary>①的结果*/
        SELECT *
        FROM employees a
        WHERE a.salary > (SELECT salary
                              FROM employees
                              WHERE last_name = 'Abel');
      

        多个标量子查询,示例:返回job_id与141号员工相同,salary比143号员工多的员工、姓名、job_id和工资

        /*③查询员工的姓名、job_id、工资,要求job_id=① and salary>②*/
        SELECT
          a.last_name 姓名,
          a.job_id,
          a.salary    工资
        FROM employees a
        WHERE a.job_id = (SELECT job_id
                              FROM employees
                              WHERE employee_id = 141)
                AND
                a.salary > (SELECT salary
                              FROM employees
                              WHERE employee_id = 143);
      

        子查询+分组函数,示例:查询最低工资大于50号部门最低工资的部门id和其最低工资【having】

        /*③在②的基础上筛选,满足min(salary)>①*/
        SELECT
          min(a.salary) minsalary,
          department_id
        FROM employees a
        GROUP BY a.department_id
        HAVING min(a.salary) > (SELECT min(salary)
                                    FROM employees
                                    WHERE department_id = 50);
        

        错误的标量子查询,示例:将上面的示例③中子查询语句中的min(salary)改为salary,执行效果如下:

        SELECT
                min(a.salary) minsalary,
                department_id
              FROM employees a
              GROUP BY a.department_id
              HAVING min(a.salary) > (SELECT salary
                                          FROM employees
                                          WHERE department_id = 500000);
      ERROR 1242 (21000): Subquery returns more than 1 row
      

      错误提示:子查询返回的结果超过了1行记录。

      说明:上面的子查询只支持最多一列一行记录。  

      列子查询  

      列子查询需要搭配多行操作符使用:in(not in)、any/some、all。

      为了提升效率,最好去重一下distinct关键字。    

      示例1:返回location_id是1400或1700的部门中的所有员工姓名

      /*返回location_id是1400或1700的部门中的所有员工姓名*/
      /*方式1*/
      /*①查询location_id是1400或1700的部门编号*/
      SELECT DISTINCT department_id
      FROM departments
      WHERE location_id IN (1400, 1700);

      /*②查询员工姓名,要求部门是①列表中的某一个*/
      SELECT a.last_name
      FROM employees a
      WHERE a.department_id IN (SELECT DISTINCT department_id
                                      FROM departments
                                      WHERE location_id IN (1400, 1700));

      /*方式2:使用any实现*/
      SELECT a.last_name
      FROM employees a
      WHERE a.department_id = ANY (SELECT DISTINCT department_id
                                          FROM departments
                                          WHERE location_id IN (1400, 1700));

      /*拓展,下面与not in等价*/
      SELECT a.last_name
      FROM employees a
      WHERE a.department_id <> ALL (SELECT DISTINCT department_id
                                          FROM departments
                                          WHERE location_id IN (1400, 1700));
      

      示例2:返回其他工种中比job_id为'IT_PROG'工种任意工资低的员工的员工号、姓名、job_id、salary

      /*返回其他工种中比job_id为'IT_PROG'工种任一工资低的员工的员工号、姓名、job_id、salary*/
      /*①查询job_id为'IT_PROG'部门任-工资*/
        SELECT DISTINCT salary
        FROM employees
        WHERE job_id = 'IT_PROG';

      /*②查询员工号、姓名、job_id、salary,slary<①的任意一个*/
        SELECT
          last_name,
          employee_id,
          job_id,
          salary
        FROM employees
        WHERE salary < ANY (SELECT DISTINCT salary
                                  FROM employees
                                  WHERE job_id = 'IT_PROG') AND job_id != 'IT_PROG';

      /*或者*/
        SELECT
          last_name,
          employee_id,
          job_id,
          salary
        FROM employees
        WHERE salary < (SELECT max(salary)
                            FROM employees
                            WHERE job_id = 'IT_PROG') AND job_id != 'IT_PROG';
      

      示例3:返回其他工种中比job_id为'IT_PROG'部门所有工资低的员工的员工号、姓名、job_id、salary

      /*返回其他工种中比job_id为'IT_PROG'部门所有工资低的员工的员工号、姓名、job_id、salary*/
        SELECT
          last_name,
          employee_id,
          job_id,
          salary
        FROM employees
        WHERE salary < ALL (SELECT DISTINCT salary
                                  FROM employees
                                  WHERE job_id = 'IT_PROG') AND job_id != 'IT_PROG';

      /*或者*/
        SELECT
          last_name,
          employee_id,
          job_id,
          salary
        FROM employees
        WHERE salary < (SELECT min(salary)
                            FROM employees
                            WHERE job_id = 'IT_PROG') AND job_id != 'IT_PROG';

      行子查询(结果集一行多列)   

        示例:查询员工编号最小并且工资最高的员工信息,3种方式。 
        /*查询员工编号最小并且工资最高的员工信息*/
        /*①查询最小的员工编号*/
        SELECT min(employee_id)
        FROM employees;
        /*②查询最高工资*/
        SELECT max(salary)
        FROM employees;
        /*③方式1:查询员工信息*/
        SELECT *
        FROM employees a
        WHERE a.employee_id = (SELECT min(employee_id)
                           FROM employees)
          AND salary = (SELECT max(salary)
                        FROM employees);

        /*方式2*/
        SELECT *
        FROM employees a
        WHERE (a.employee_id, a.salary) = (SELECT
                                         min(employee_id),
                                         max(salary)
                                       FROM employees);
        /*方式3*/
        SELECT *
        FROM employees a
        WHERE (a.employee_id, a.salary) in (SELECT
                                         min(employee_id),
                                         max(salary)
                                       FROM employees);

      exists后面(也叫做相关子查询)    

    1. 语法:exists(玩转的查询语句)。

    2. exists查询结果:1或0,exists查询的结果用来判断子查询的结果集中是否有值。

    3. 一般来说,能用exists的子查询,绝对都能用in代替,所以exists用的少。

    4. 和前面的查询不同,这先执行主查询,然后主查询查询的结果,在根据子查询进行过滤,子查询中涉及到主查询中用到的字段,所以叫相关子查询。  

      示例1:简单示例
           SELECT exists(SELECT employee_id
     

                  FROM employees
                  WHERE salary = 300000)
     AS 'exists返回1或者0';
         

      示例2:查询所有员工的部门名称

        /*exists入门案例*/
        SELECT exists(SELECT employee_id
                  FROM employees
                  WHERE salary = 300000) AS 'exists返回1或者0';

        /*查询所有员工部门名*/
        SELECT department_name
        FROM departments a
        WHERE exists(SELECT 1
                         FROM employees b
                         WHERE a.department_id = b.department_id);

        /*使用in实现*/
        SELECT department_name
        FROM departments a
        WHERE a.department_id IN (SELECT department_id
                                        FROM employees);
         

        示例3:查询没有员工的部门

        /*查询没有员工的部门*/
        /*exists实现*/
        SELECT *
        FROM departments a
        WHERE NOT exists(SELECT 1
                               FROM employees b
                               WHERE a.department_id = b.department_id AND b.department_id IS NOT NULL);
        /*in的方式*/
        SELECT *
        FROM departments a
        WHERE a.department_id NOT IN (SELECT department_id
                                              FROM employees b
                                             WHERE b.department_id IS NOT NULL);

    六、NULL的大坑    

      示例1:使用in的方式查询没有员工的部门,如下:

        SELECT *
        FROM departments a
        WHERE a.department_id NOT IN (SELECT department_id
                                              FROM employees b);

        Empty set (0.00 sec)

        in的情况下,子查询中列的值为NULL的时候,外查询的结果为空。

        建议:建表是,列不允许为空。

    七、总结

    1. 本文中讲解了常见的子查询,请大家务必多练习

    2. 注意in、any、some、any的用法

    3. 字段为空的时候,in查询有大坑,这个要注意

    4. 建议创建表的时候,列不允许为空

  • 相关阅读:
    图论-最短路
    windows对拍及其应用
    RMQ与st表
    树状数组
    二分和三分题
    [转载]图论500题
    浏览器请求背后的网络数据传输过程
    百度ocr文字识别接口使用
    Mysql启动报错解决方案:Failed to open log (file './mysql-bin.000901', errno 2)
    Mac环境下nginx https配置
  • 原文地址:https://www.cnblogs.com/biao/p/11764833.html
Copyright © 2011-2022 走看看