• 数据库子查询


    • 当一个查询是另一个查询的子部分时,称之为子查询(查询语句中嵌套有查询语句)
    • 标量子查询(scalar subquery)是只返回一个 值的查询:一行,一列
    • 1. 目标列位置。
    • 子查询如果位于目标列,则只能是标量子查询,否则数据库可能返回类似“错误: 子查询必须只能返回一个字段”的提示。

      示例

    • CREATE TABLE t1 (k1 INT PRIMARY KEY, c1 INT);

      CREATE TABLE t2 (k2 INT PRIMARY KEY, c2 INT);
      INSERT INTO t2 VALUES (1,10), (2,2), (3,30);

    • mysql> SELECT t1.c1, (SELECT t2.c2 FROM t2) FROM t1, t2;
      Empty set (0.00 sec)    t1表没数据

      mysql> insert into t1 values (1,1), (2,2), (3,3);
      Query OK, 3 rows affected (0.01 sec)
      Records: 3 Duplicates: 0 Warnings: 0

      mysql> SELECT t1.c1, (SELECT t2.c2 FROM t2) FROM t1, t2;
      ERROR 1242 (21000): Subquery returns more than 1 row   子查询超过一行  标量查询只返回一个字段

      mysql> DELETE FROM T2;
      Query OK, 3 rows affected (0.01 sec)

      mysql> SELECT t1.c1, (SELECT t2.c2 FROM t2) FROM t1, t2;
      Empty set (0.00 sec)

    • 示例二
    • mysql> insert into t2 values (1,10), (2,2), (3,30);
      Query OK, 3 rows affected (0.01 sec)
      Records: 3 Duplicates: 0 Warnings: 0

      mysql> SELECT t1.c1, (SELECT t2.c2 FROM t2 WHERE K2=1) FROM t1, t2;
      +------+-----------------------------------+
      | c1 | (SELECT t2.c2 FROM t2 WHERE K2=1) |
      +------+-----------------------------------+
      | 1 | 10 |
      | 2 | 10 |
      | 3 | 10 |
      | 1 | 10 |
      | 2 | 10 |
      | 3 | 10 |
      | 1 | 10 |
      | 2 | 10 |
      | 3 | 10 |
      +------+-----------------------------------+
      9 rows in set (0.00 sec)

    • 示例三
    • mysql> SELECT t1.c1, (SELECT t2.c2 FROM t2 WHERE c2=1) FROM t1, t2;
      +------+-----------------------------------+
      | c1 | (SELECT t2.c2 FROM t2 WHERE c2=1) |
      +------+-----------------------------------+
      | 1 | NULL |
      | 2 | NULL |
      | 3 | NULL |
      | 1 | NULL |
      | 2 | NULL |
      | 3 | NULL |
      | 1 | NULL |
      | 2 | NULL |
      | 3 | NULL |
      +------+-----------------------------------+
      9 rows in set (0.00 sec)

      mysql> SELECT t1.c1, (SELECT t2.c2 FROM t2 WHERE c2>1) FROM t1, t2;
      ERROR 1242 (21000): Subquery returns more than 1 row    行级不能作为目标列

    • 2 FROM子句位置。
      相关子查询出现在FROM子句中,数据库可能返回类似“在FROM子句中的子查询无法参考相同查询级别中的关系”的提示,所以相关子查询不能出现在FROM子句中;
      非相关子查询出现在FROM子句中,可上拉子查询到父层,在多表连接时统一考虑连接代价然后择优。

    • mysql> SELECT * FROM t1, (SELECT * FROM t2 WHERE t1.k1=t2.k2);
      ERROR 1248 (42000): Every derived table must have its own alias  相关子查询 相关子查询不能出现在FROM子句中

      mysql> SELECT * FROM t1, (SELECT * FROM t2 WHERE t1.k1=t2.k2) AS A_t12;
      ERROR 1054 (42S22): Unknown column 't1.k1' in 'where clause'

    • 改造后
    • mysql> SELECT * FROM t1, (SELECT * FROM t2);   去掉where子查询  变为非相关子查询
      ERROR 1248 (42000): Every derived table must have its own alias

      mysql> SELECT * FROM t1, (SELECT * FROM t2) as A_t2;
      +----+------+----+------+
      | k1 | c1 | k2 | c2 |
      +----+------+----+------+
      | 1 | 1 | 1 | 10 |
      | 2 | 2 | 1 | 10 |
      …...
      | 2 | 2 | 4 | 10 |
      | 3 | 3 | 4 | 10 |
      +----+------+----+------+
      12 rows in set (0.00 sec)

    • 3 WHERE子句位置。
      出现在WHERE子句中的子查询,是一个条件表达式的一部分,而表达式可以分解为操作符和操作数;根据参与运算的不同的数据类型,操作符也不尽相同,如INT型有“>、<、=、<>”等操作,这对子查询均有一定的要求(如INT型的等值操作,要求子查询必须是标量子查询)。另外,子查询出现在WHERE子句中的格式,也有用谓词指定的一些操作,如IN、BETWEEN、EXISTS等。

    • mysql> SELECT * FROM t1 WHERE k1 IN (SELECT k2 FROM t2);
      +----+------+
      | k1 | c1 |
      +----+------+
      | 1 | 1 |
      | 2 | 2 |
      | 3 | 3 |
      +----+------+
      3 rows in set (0.00 sec)

    • SELECT * FROM t1 WHERE k1 >=ANY (SELECT k2 FROM t2);
      SELECT * FROM t1 WHERE k1 <=SOME (SELECT k2 FROM t2);
      SELECT * FROM t1 WHERE NOT EXISTS (SELECT k2 FROM t2 WHERE k2=100);

    • 4 JOIN/ON子句位置。
      JOIN/ON子句可以拆分为两部分,一是JOIN块类似于FROM子句,二是ON子句块类似于WHERE子句,这两部分都可以出现子查询。子查询的处理方式同FROM子句和WHERE子句。

    • 5 GROUPBY子句位置
      目标列必须和GROUPBY关联1。可将子查询写在GROUPBY位置处,但子查询用在GROUPBY处没有实用意义。

    • 6 ORDERBY子句位置。
      可将子查询写在ORDERBY位置处。但ORDERBY操作是作用在整条SQL语句上的,子查询用在ORDERBY处没有实用意义。

    • 子查询的类型---从对象间的关系看:
    • 1 相关子查询。

        子查询的执行依赖于外层父查询的一些属性值。子查询因依赖于父查询的参数,

        当父查询的参数改变时,子查询需要根据新参数值重新执行(查询优化器对相关子查询进行优化

        有一定意义),如:

        SELECT * FROM t1 WHERE col_1 = ANY
        (SELECT col_1 FROM t2 WHERE t2.col_2 = t1.col_2);
        /* 子查询语句中存在父查询的t1表的col_2列 */

      

    • 2 非相关子查询。

        子查询的执行,不依赖于外层父查询的任何属性值。这样子查询具有独立性,可独自求解,

        形成一个子查询计划先于外层的查询求解,如:

        SELECT * FROM t1 WHERE col_1 = ANY
        (SELECT col_1 FROM t2 WHERE t2.col_2 = 10);
        //子查询语句中(t2)不存在父查询(t1)的属性

    • 3 相关子查询与非相关子查询的优化对比。

        扩展阅读:
        相关子查询与不相关子查询的优化(一)
          http://blog.163.com/li_hx/blog/static/1839914132014248292396/

        相关子查询与不相关子查询的优化(二)
          http://blog.163.com/li_hx/blog/static/1839914132014251031411/

        相关子查询与不相关子查询的优化(三)
          http://blog.163.com/li_hx/blog/static/183991413201421075949661/

    子查询的类型---从特定谓词看

      1 [NOT] IN/ALL/ANY/SOME子查询。
       语义相近,表示“[取反] 存在/所有/任何/任何”,左面是操作数,右面是子查询,是最常见的子查询类型之一。

      2[NOT] EXISTS子查询。
       半连接语义,表示“[取反] 存在”,没有左操作数,右面是子查询,也是最常见的子查询类型之一。

      3 其他子查询。
       除了上述两种外的所有子查询。

    子查询的类型---从语句的构成复杂程度看

      1 SPJ子查询。
      由选择、连接、投影操作组成的查询。

      2 GROUPBY子查询。
      SPJ子查询加上分组、聚集操作组成的查询。

      3 其他子查询。
      GROUPBY子查询中加上其他子句如Top-N 、LIMIT/OFFSET、集合、排序等操作。

      后两种子查询有时合称非SPJ子查询

    子查询的类型---从结果的角度看:

      1 标量子查询。
      子查询返回的结果集类型是一个简单值(return a scalar,a single value)。

      2 单行单列子查询。
      子查询返回的结果集类型是零条或一条单元组(return a zero or single row,but only a column)。

      相似于标量子查询,但可能返回零条元组。

      3 多行单列子查询。
      子查询返回的结果集类型是多条元组但只有一个简单列(return multiple rows,but only a column)。

      4 表子查询。
      子查询返回的结果集类型是一个表(多行多列)

      (returna table,one or more rows of one or more columns)。

    在数据库实现早期,查询优化器对子查询一般采用嵌套执行的方式,即对父查询中的每一行,都执行一次子查询,这样子查询会执行很多次。这种执行方式效率很低。

      而对子查询进行优化,可能带来几个数量级的查询效率的提高。
      子查询转变成为连接操作之后,会得到如下好处:
        1 子查询不用执行很多次。
        2 优化器可以根据统计信息来选择不同的连接方法和不同的连接顺序。

        子查询中的连接条件、过滤条件分别变成了父查询的连接条件、过滤条件,

        优化器可以对这些条件进行下推,以提高执行效率。

    • 1 子查询合并(Subquery Coalescing)

      在某些条件下(语义等价:两个查询块产生同样的结果集),多个子查询能够合并成一个子查询

      (合并后还是子查询,以后可以通过其他技术消除掉子查询)。

      这样可以把多次表扫描、多次连接减少为单次表扫描和单次连接,如:

      SELECT * FROM t1 WHERE a1<10 AND (

      EXISTS (SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=1) OR 

      EXISTS (SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=2)
      );

    可优化为:

      SELECT * FROM t1 WHERE a1<10 AND (
      EXISTS (SELECT a2 FROM t2 WHERE t2.a2<5 AND (t2.b2=1 OR t2.b2=2)
      /*两个ESISTS子句合并为一个,条件也进行了合并 */

    • 2 子查询展开(Subquery Unnesting)。

      又称子查询反嵌套,又称为子查询上拉。
      把一些子查询置于外层的父查询中,作为连接关系与外层父查询并列,其实质是把某些子查询重写为等价的多表连接操作(展开后,子查询不存在了,外部查询变成了多表连接)。

    带来的好处是,有关的访问路径、连接方法和连接顺序可能被有效使用,使得查询语句的层次尽可能的减少。

      常见的IN/ANY/SOME/ALL/EXISTS依据情况转换为半连接(SEMI JOIN)、普通类型的子查询消除等情况属于此类,如:

      SELECT * FROM t1, (SELECT * FROM t2 WHERE t2.a2 >10) v_t2
      WHERE t1.a1<10 AND v_t2.a2<20;

    可优化为:

      SELECT * FROM t1, t2 WHERE t1.a1<10 AND t2.a2<20 AND t2.a2 >10;
      /* 子查询变为了t1、t2表的连接操作,相当于把t2表从子查询中上拉了一层 */

    • 3 聚集子查询消除(Aggregate Subquery Elimination)。

      通常,一些系统支持的是标量聚集子查询消除。

      如:
      SELECT * FROM t1 WHERE t1.a1>(SELECT avg(t2.a2) FROM t2);

    优化的条件:
      1 如果子查询中出现了聚集、GROUPBY、DISTINCT子句,则子查询只能单独求解,不可以上拉到外层。
      2 如果子查询只是一个简单格式的(SPJ格式)查询语句,则可以上拉子查询到外层,这样往往能提高查询效率。子查询上拉,讨论的就是这种格式,这也是子查询展开技术处理的范围。

    把子查询上拉到上层查询,前提是上拉(展开)后的结果不能带来多余的元组,所以子查询展开需要遵循如下规则:

      1 如果上层查询的结果没有重复(即SELECT子句中包含主码),则可以展开其子查询。并且展开后的查询的  SELECT子句前应加上DISTINCT标志。

      2 如果上层查询的SELECT语句中有DISTINCT标志,可以直接进行子查询展开。
      如果内层查询结果没有重复元组,则可以展开。

    子查询展开的具体步骤:

    1. ) 将子查询和外层查询的FROM子句连接为同一个FROM子句,并且修改相应的运行参数。
    2. ) 将子查询的谓词符号进行相应修改(如:“IN”修改为“=”)。
    3. ) 将子查询的WHERE条件作为一个整体与外层查询的WHERE条件合并,并用AND条件连接词连接,从而保证新生成的谓词与原旧谓词的上下文意思相同,且成为一个整体

     参考:


      https://www.cnblogs.com/yanyiyaner/diary/2018/06/20/9203328.html

  • 相关阅读:
    LUA表的引用理解
    UNITY 打包安卓APK
    UNITY打包问题
    U3D 打包时找不到tag的问题
    break prefab instance的原理
    C#调用LUA函数
    LUA 利用#遍历表的问题
    U3D笔记11:47 2016/11/30-15:15 2016/12/19
    Unity3D 预设打包的注意事项
    回调 和 覆盖
  • 原文地址:https://www.cnblogs.com/yanyiyaner/p/9203130.html
Copyright © 2020-2023  润新知