zoukankan      html  css  js  c++  java
  • 012:子查询和增删改查

    一. 子查询

    子查询就是指在一个select语句中嵌套另一个select语句。同时,子查询必须包含括号

    MySQL 5.6.x 版本之前,MySQL的子查询性能较差,但是从5.6开始,不存在性能差的问题。

    select a from t1 where a > any(select a from t2);
    
    1. select a from t1 是外部查询(outer query)
    2. (select a from t2) 是子查询(subquery)

    一般说来,子查询嵌套于外部查询中,可以将两个或两个以上的子查询进行嵌套

    1. 子查询的使用

    1.1. ANY / SOME

    如果外部查询的列的结果和子查询的列的结果比较得到为True的话,则返回比较值为True的外查询的记录

    (gcdb@localhost) 17:37:05 [mytest]> select * from t1;
    +------+
    | a    |
    +------+
    |    1 |
    |    3 |
    |    4 |
    |    5 |
    |    7 |
    +------+
    5 rows in set (0.00 sec)
    
    (gcdb@localhost) 17:37:22 [mytest]> select * from t2;
    +------+
    | a    |
    +------+
    |    2 |
    |    4 |
    |    8 |
    |   10 |
    +------+
    4 rows in set (0.00 sec)
    
    (gcdb@localhost) 17:39:33 [mytest]> select a from t1 where a > any(select a from t2);
    +------+
    | a    |
    +------+
    |    3 |
    |    4 |
    |    5 |
    |    7 |
    +------+
    4 rows in set (0.00 sec)
    
    -- 这个查询可以解释为,t1表内a列的值大于t2表中a列的`任意(any)`一个值`t1.a > any(t2.a) == true`,则返回t1.a的记录
    

    ANY关键词必须与一个比较操作符一起使用: =, >, <, >=, <=, <> (这个是!=的意思)

    子查询中SOMEANY是同一个意思

    1.2. IN

    inANY的一种特殊情况:"in" equals "= any"

    (gcdb@localhost) 17:58:21 [mytest]> select a from t1 where a = any(select a from t2); -- t1.a==t2.a 的只有4
    +------+
    | a    |
    +------+
    |    4 |
    +------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 17:57:01 [mytest]>  select a from t1 where a in (select a from t2);  -- in的结果等同于 =any 的结果
    +------+
    | a    |
    +------+
    |    4 |
    +------+
    1 row in set (0.00 sec)
    
    

    select a from s1 where a in (select a in t2);是用的比较多的一种语法

    1.3. ALL

    如果外部查询的列的结果和子查询的列的所有结果比较得到为True的话,则返回比较值为True的(外查询)的记录

    (gcdb@localhost) 17:55:03 [mytest]> select a from t2 where a >all(select a from t1);
    +------+
    | a    |
    +------+
    |    8 |
    |   10 |
    +------+
    2 rows in set (0.00 sec)
    
    

    ALL关键词必须与一个比较操作符一起使用
    NOT IN<> ALL的别名

    2. 子查询的分类

    • 独立子查询

      • 不依赖外部查询而运行的子查询
      
      (gcdb@localhost) 18:00:04 [mytest]> select a from t1 where a in (1,2,3,4,5);
      +------+
      | a    |
      +------+
      |    1 |
      |    3 |
      |    4 |
      |    5 |
      +------+
      4 rows in set (0.00 sec)
      
      
    • 相关子查询

      • 引用了外部查询列的子查询
      -- 在这个例子中,子查询中使用到了外部的列t2.a 
      
      (gcdb@localhost) 18:00:48 [mytest]>  select a from t1 where a in (select * from t2 where t1.a = t2.a);
      +------+
      | a    |
      +------+
      |    4 |
      +------+
      1 row in set (0.00 sec)
      
      

    3. 子查询的优化

    • MySQL5.6之前

      • MySQL5.6之前,优化器会把子查询重写成exists的形式
      select a from t1 where a in (select a from t2); -- 这个是一条独立的子查询,时间复杂度 O(M+N)
      --
      -- 经过优化器重写后
      --
      select a from t1 where exists (select 1 from t2 where t1.a = t2.a); -- 这是相关子查询,复杂度O(M*N + M)
      
      • MySQL 5.6之前,部分的子查询需要重写成join的形式 (注意表的大小)
      mysql> select t1.a from t1 join t2 on t1.a = t2.a;
      +------+
      | a    |
      +------+
      |    4 |
      +------+
      1 row in set (0.00 sec)
      
    • MySQL 5.6之后

    MySQL 5.6之后,优化器不会将子查询重写exists的形式,而是自动优化,性能有了大幅提升

    4. 包含NULL值的NOT IN

    mysql> select null in ('a', 'b', null);
    +--------------------------+
    | null in ('a', 'b', null) |
    +--------------------------+
    |                     NULL |
    +--------------------------+
    1 row in set (0.00 sec)
    

    MySQL数据库的比较操作,除了返回1(True), 0(False)之外,还会返回NULL
    NULLNULL的比较,返回的还是NULL

    mysql> select null not in ('a', 'b', null);  
    +------------------------------+
    | null not in ('a', 'b', null) |
    +------------------------------+
    |                         NULL |  -- null不在('a', 'b', null)中,返回的还是null,因为有null和null的比较
    +------------------------------+
    1 row in set (0.00 sec)
    
    mysql> select 'a' not in ('a', 'b', null);  
    +-----------------------------+
    | 'a' not in ('a', 'b', null) |
    +-----------------------------+
    |                           0 |  -- a 不在 ('a', 'b', null)中,返回0,即False
    +-----------------------------+
    1 row in set (0.00 sec)
    
    mysql> select 'c' not in ('a', 'b');
    +-----------------------+
    | 'c' not in ('a', 'b') |
    +-----------------------+
    |                     1 |  -- 这个返回值可以理解 'c'不在('a', 'b')中,返回1,即为True
    +-----------------------+
    1 row in set (0.00 sec)
    
    mysql> select 'c' not in ('a', 'b', null); 
    +-----------------------------+
    | 'c' not in ('a', 'b', null) |
    +-----------------------------+
    |                        NULL | -- 理论上应该是返回1,即True的。但是包含了null值。则返回null
    +-----------------------------+
    1 row in set (0.00 sec)
    

    对于包含了NULL值的IN操作,总是返回True或者NULL
    NOT IN返回NOT True (False)或者NOT NULL (NULL)

    --
    -- SQL语句一 使用 EXISTS
    --
    select customerid, companyname 
        from customers as A
        where country = 'Spain' 
            and not exists
                ( select * from orders as B
                  where A.customerid = B.customerid );
                  
    --
    -- SQL语句二 使用 IN
    --
    select customerid, companyname 
        from customers as A
        where country = 'Spain' 
            and customerid not in (select customerid from orders);
                  
    -----
    -- 当结果集合中没有NULL值时,上述两条SQL语句查询的结果是一致的 
    -----
    
    --
    -- 插入一个NULL值
    --
    insert into orders(orderid) values (null);
    
    -----
    -- SQL语句1 : 返回和之前一致
    -- SQL语句2 : 返回为空表,因为子查询返回的结果集中存在NULL值。not in null 永远返回False或者NULL
    --            此时 where (country = 'Spain' and (False or NULL)) 为 False OR NULL,条件永远不匹配
    -----
    
    --
    -- SQL语句2 改写后
    --
    select customerid, companyname 
        from customers as A
        where country = 'Spain' 
            and customerid not in (select customerid from orders 
                                    where customerid is not null);  -- 增加这个过滤条件,使用is not,而不是<>
    
    
    --
    -- 和 null比较,使用is和is not, 而不是 = 和 <>
    --
    mysql> select null = null; 
    +-------------+
    | null = null |
    +-------------+
    |        NULL |
    +-------------+
    1 row in set (0.00 sec)
    
    mysql> select null <> null;
    +--------------+
    | null <> null |
    +--------------+
    |         NULL |
    +--------------+
    1 row in set (0.00 sec)
    
    mysql> select null is null; 
    +--------------+
    | null is null |
    +--------------+
    |            1 |  -- 返回 True
    +--------------+
    1 row in set (0.00 sec)
    
    mysql> select null is not  null;
    +-------------------+
    | null is not  null |
    +-------------------+
    |                 0 |  -- 返回 False
    +-------------------+
    1 row in set (0.00 sec)
    

    EXISTS不管返回值是什么,而是看是否有返回,所以EXISTS中子查询都是select *select 1等,因为只关心返回是否有行(结果集)


    二. INSERT

    官方INSERT文

    (gcdb@localhost) 10:46:17 [mytest]> select * from t1;
    +------+
    | a    |
    +------+
    |    1 |
    |    3 |
    |    4 |
    |    5 |
    |    7 |
    +------+
    5 rows in set (0.00 sec)
    
    (gcdb@localhost) 10:46:36 [mytest]> insert into t1 values(9);  -- 插入一个值
    Query OK, 1 row affected (0.01 sec)
    
    (gcdb@localhost) 10:46:44 [mytest]> insert into t1(a) values(9),(11);    -- 插入多个值,MySQL独有
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 10:47:11 [mytest]> insert into t1(a) select 13;  -- insert XXX select XXX 语法,MySQ独有
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 10:53:34 [mytest]> select * from t1;
    +------+
    | a    |
    +------+
    |    1 |
    |    3 |
    |    4 |
    |    5 |
    |    7 |
    |    9 |
    |   11 |
    |   13 |
    +------+
    8 rows in set (0.00 sec)
    
    (gcdb@localhost) 10:46:52 [mytest]> create table t3(a int, b int);  -- 有多个列
    Query OK, 0 rows affected (0.15 sec)
    
    (gcdb@localhost) 10:47:24 [mytest]> insert into t3 select 12;   -- 没有指定列,报错
    ERROR 1136 (21S01): Column count doesn't match value count at row 1
    
    (gcdb@localhost) 10:47:48 [mytest]> insert into t3(a) select 12;   -- 指定列a
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 10:48:08 [mytest]> insert into t3(a,b) select 11,12;  -- 不指定列,但是插入值匹配列的个数和类型
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 10:48:21 [mytest]> select * from t3;
    +------+------+
    | a    | b    |
    +------+------+
    |   12 | NULL |
    |   11 |   12 |
    +------+------+
    2 rows in set (0.00 sec)
    
    (gcdb@localhost) 10:49:19 [mytest]> insert into t3 select * from t3;   -- 从t3表中查询数据并插回t3中
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 10:55:49 [mytest]> insert into t3(a) select a from t2;  -- 从t2表中查询数据并插入到t3(a)中,注意指定列
    Query OK, 4 rows affected (0.00 sec)
    Records: 4  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 10:59:02 [mytest]> select * from t3;
    +------+------+
    | a    | b    |
    +------+------+
    |   12 | NULL |
    |   11 |   12 |
    |   12 | NULL |
    |   11 |   12 |
    |    2 | NULL |
    |    4 | NULL |
    |    8 | NULL |
    |   10 | NULL |
    +------+------+
    8 rows in set (0.00 sec)
    
    --
    -- 如果想快速增长表格中的数据,可以使用如下方法,使得数据成倍增长
    --
    mysql> insert into t3 select * from t3;
    Query OK, 5 rows affected (0.03 sec)  -- 插入了5列
    Records: 5  Duplicates: 0  Warnings: 0
    
    mysql> select * from t3;               
    +------+------+
    | a    | b    |
    +------+------+
    |    8 | NULL |
    |    8 |    9 |
    | NULL |    5 |
    | NULL |    4 |
    | NULL |    3 |
    |    8 | NULL |
    |    8 |    9 |
    | NULL |    5 |
    | NULL |    4 |
    | NULL |    3 |
    +------+------+
    10 rows in set (0.00 sec)
    
    

    三. DELETE

    官方DELETE文档

    (gcdb@localhost) 11:02:10 [mytest]> delete from t3 where b is null; -- 根据过滤条件删除
    Query OK, 6 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:02:16 [mytest]> select * from t3;
    +------+------+
    | a    | b    |
    +------+------+
    |   11 |   12 |
    |   11 |   12 |
    +------+------+
    2 rows in set (0.00 sec)
    
    
    (gcdb@localhost) 11:02:48 [mytest]> delete from t3;   -- 删除整个表
    Query OK, 2 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:03:17 [mytest]> select * from t3;
    Empty set (0.00 sec)
    
    

    四. UPDATE

    官方UPDATE文档

    (gcdb@localhost) 11:03:19 [mytest]> insert into t3 select 1,2;
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 11:03:54 [mytest]> insert into t3 select 2,3;
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 11:03:58 [mytest]> insert into t3 select 3,4;
    Query OK, 1 row affected (0.00 sec)
    Records: 1  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 11:04:02 [mytest]> select * from t3;
    +------+------+
    | a    | b    |
    +------+------+
    |    1 |    2 |
    |    2 |    3 |
    |    3 |    4 |
    +------+------+
    3 rows in set (0.00 sec)
    (gcdb@localhost) 11:04:30 [mytest]> update t3 set a =10 where a=1;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    (gcdb@localhost) 11:05:02 [mytest]>  select * from t3;
    +------+------+
    | a    | b    |
    +------+------+
    |   10 |    2 |
    |    2 |    3 |
    |    3 |    4 |
    +------+------+
    3 rows in set (0.00 sec)
    
    --
    -- 关联后更新
    --
    
    (gcdb@localhost) 11:05:07 [mytest]> select * from t2;
    +------+
    | a    |
    +------+
    |    2 |      --和t3中的a列2相等
    |    4 |
    |    8 |
    |   10 |      --和t3中的a列10相等
    +------+
    4 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:06:05 [mytest]> select * from t3;
    +------+------+
    | a    | b    |
    +------+------+
    |   10 |    2 |   -- 和t2中的10相等
    |    2 |    3 |   -- 和t2中的2相等
    |    3 |    4 |
    +------+------+
    3 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:08:08 [mytest]> update t2 join t3 on t2.a = t3.a  set t2.a=1000;   -- 先得到t2.a=t3.a的结果然后将结果集中的t2.a设置为100
    Query OK, 2 rows affected (0.00 sec)
    Rows matched: 2  Changed: 2  Warnings: 0
    
    (gcdb@localhost) 11:08:16 [mytest]> select * from t2;
    +------+
    | a    |
    +------+
    | 1000 |   -- 该行原先2被更新成100
    |    4 |
    |    8 |
    | 1000 |   -- 该行原先10被更新成100
    +------+
    4 rows in set (0.01 sec)
    

    五. REPLACE

    官方REPLACE文档

    (gcdb@localhost) 11:18:41 [mytest]> create table t4(a int primary key auto_increment,b int);
    Query OK, 0 rows affected (0.01 sec)
    
    (gcdb@localhost) 11:18:47 [mytest]> insert into t4 values(null,1);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 11:19:17 [mytest]> insert into t4 values(null,2);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 11:19:19 [mytest]> insert into t4 values(null,3);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 11:19:22 [mytest]> select * from t4;
    +---+------+
    | a | b    |
    +---+------+
    | 1 |    1 |
    | 2 |    2 |
    | 3 |    3 |
    +---+------+
    3 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:20:01 [mytest]> insert into t4 values(1,10);    -- error,主键值1,重复 
    ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
    
    (gcdb@localhost) 11:20:28 [mytest]> replace into t4 values(1,10);   -- 替换该主键对应的值  
    Query OK, 2 rows affected (0.00 sec)                                -- 两行记录受到影响
    
    (gcdb@localhost) 11:20:54 [mytest]> select * from t4;
    +---+------+
    | a | b    |
    +---+------+
    | 1 |   10 |     -- 已经被更新
    | 2 |    2 |
    | 3 |    3 |
    +---+------+
    3 rows in set (0.00 sec)
    
    -----
    -- replace的原理是:先delete,在insert ;注意:需要delete和insert权限
    -----
    
    (gcdb@localhost) 11:24:23 [mytest]> replace into t4 values(5,15);   -- 没有替换对象时,类似插入效果
    Query OK, 1 row affected (0.03 sec)    -- 只影响1行
    
    (gcdb@localhost) 11:24:32 [mytest]> select * from t4;
    +---+------+
    | a | b    |
    +---+------+
    | 1 |   10 |
    | 2 |    2 |
    | 3 |    3 |
    | 5 |   15 |         -- 新插入一行
    +---+------+
    4 rows in set (0.00 sec)
    
    --
    -- replace原理更明显的例子 
    --
    
    (gcdb@localhost) 11:24:35 [mytest]> create table t6
        -> (a int primary key,
        -> b int auto_increment,  -- b是auto_increment的int型数据
        -> c int,key(b));
    Query OK, 0 rows affected (0.01 sec)
    
    
    (gcdb@localhost) 11:26:54 [mytest]> insert into t6 values(1,null,10),(2,null,20),(3,null,30);
    Query OK, 3 rows affected (0.00 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 11:28:08 [mytest]> select * from t6;  
    +---+---+------+
    | a | b | c    |    --b类为自增列
    +---+---+------+
    | 1 | 1 |   10 |   
    | 2 | 2 |   20 |
    | 3 | 3 |   30 |
    +---+---+------+
    3 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:29:14 [mytest]> replace into t6 values(1,null,99);
    Query OK, 2 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:29:40 [mytest]> select * from t6;
    +---+---+------+
    | a | b | c    |
    +---+---+------+
    | 1 | 4 |   99 |    --当a=1时,c列值10被替换为99和b列值3自增加1为4
    | 2 | 2 |   20 |
    | 3 | 3 |   30 |
    +---+---+------+
    3 rows in set (0.00 sec)
    -----
    
    --
    -- insert on duplicate 效果和 replace类似
    --
    (gcdb@localhost) 11:29:50 [mytest]> select * from t4;
    +---+------+
    | a | b    |
    +---+------+
    | 1 |   10 |
    | 2 |    2 |
    | 3 |    3 |
    | 5 |   15 |
    +---+------+
    4 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:33:31 [mytest]> insert into t4 values(1,1); -- 插入报错,存在key为1的记录
    ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
    
    (gcdb@localhost) 11:34:26 [mytest]> insert into t4 values(1,1) on duplicate key update b=1;  -- 带上on duplicate参数(非SQL标准,不推荐)
    Query OK, 2 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:34:31 [mytest]> select * from t4;
    +---+------+
    | a | b    |
    +---+------+
    | 1 |    1 |   - 该行的b列从10被替换成1
    | 2 |    2 |
    | 3 |    3 |
    | 5 |   15 |
    +---+------+
    4 rows in set (0.00 sec)
    
    --
    -- insert ignore
    --
    (gcdb@localhost) 11:34:34 [mytest]>  insert ignore into t4 values(1,1);  -- 忽略重复的错误
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    
    (gcdb@localhost) 11:36:37 [mytest]> show warnings;
    +---------+------+---------------------------------------+
    | Level   | Code | Message                               |
    +---------+------+---------------------------------------+
    | Warning | 1062 | Duplicate entry '1' for key 'PRIMARY' |
    +---------+------+---------------------------------------+
    1 row in set (0.00 sec)
    

    六. UNION

    1. UNION的作用是将两个查询的结果集进行合并
    2. UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔。
    3. UNION中的每个查询必须包含相同的列(类型相同或可以隐式转换)、表达式或聚集函数。
    
    (gcdb@localhost) 11:14:02 [mytest]> create table t_union01(a int,b int);
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:14:31 [mytest]> create table t_union02(a int,b int);
    Query OK, 0 rows affected (0.01 sec)
    
    (gcdb@localhost) 11:15:41 [mytest]> insert into t_union01 values(1,2),(2,3),(3,4),(99,100);
    Query OK, 4 rows affected (0.01 sec)
    Records: 4  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 11:16:04 [mytest]> insert into t_union02 values(10,20),(20,30),(30,40),(99,100);
    Query OK, 4 rows affected (0.00 sec)
    Records: 4  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 11:16:26 [mytest]> select * from t_union01;
    +------+------+
    | a    | b    |
    +------+------+
    |    1 |    2 |
    |    2 |    3 |
    |    3 |    4 |
    |   99 |  100 |  --t_union01 中的99, 100
    +------+------+
    4 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:16:42 [mytest]> select * from t_union02;
    +------+------+
    | a    | b    |
    +------+------+
    |   10 |   20 |
    |   20 |   30 |
    |   30 |   40 |
    |   99 |  100 |  --t_union02 中的99, 100
    +------+------+
    4 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:22:16 [mytest]> select * from t_union02
        -> union
        -> select * from t_union01;
    +------+------+
    | a    | b    |
    +------+------+
    |   10 |   20 |
    |   20 |   30 |
    |   30 |   40 |
    |   99 |  100 |   -- 只出现了一次 99, 100,union会去重
    |    1 |    2 |
    |    2 |    3 |
    |    3 |    4 |
    +------+------+
    7 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:22:35 [mytest]> select * from t_union02  union all  select * from t_union01;   -- 使用 union all 显示不去重
    +------+------+
    | a    | b    |
    +------+------+
    |   10 |   20 |
    |   20 |   30 |
    |   30 |   40 |
    |   99 |  100 |
    |    1 |    2 |
    |    2 |    3 |
    |    3 |    4 |
    |   99 |  100 |
    +------+------+
    8 rows in set (0.00 sec)
    
    

    如果知道数据本身具有唯一性,没有重复,则建议使用union all,因为union会做去重操作,性能会比union all要低


    七. 关联更新和行号查询

    7.1. 关联更新

    (gcdb@localhost) 11:38:36 [mytest]> create table t5 (a int, b int);
    Query OK, 0 rows affected (0.14 sec)
    
    (gcdb@localhost) 11:39:38 [mytest]> insert into t5 values(1,1);
    Query OK, 1 row affected (0.03 sec)
    
    (gcdb@localhost) 11:39:49 [mytest]> select * from t5;
    +------+------+
    | a    | b    |
    +------+------+
    |    1 |    1 |
    +------+------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 11:39:51 [mytest]> update t5 set a= a+1,b=a where a=1;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    (gcdb@localhost) 11:40:03 [mytest]> select * from t5;
    +------+------+
    | a    | b    |
    +------+------+
    |    2 |    2 |  -- SQL Server和Oracle中得到的值是2, 1
    +------+------+
    1 row in set (0.00 se
    

    7.2. 显示行号(RowNumber)

    --
    -- 方法一
    --
    (gcdb@localhost) 11:41:08 [mytest]> use employees;
    Database changed
    
    (gcdb@localhost) 11:42:15 [employees]> set @rn:=0;   -- 产生 SESSION(会话)级别的变量
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:42:21 [employees]> select @rn:=@rn+1 as rownumber, emp_no, gender from employees limit 10;  -- @rn:=1 是赋值的意思
    +-----------+--------+--------+
    | rownumber | emp_no | gender |
    +-----------+--------+--------+
    |         1 |  10001 | M      |
    |         2 |  10002 | F      |
    |         3 |  10003 | M      |
    |         4 |  10004 | M      |
    |         5 |  10005 | M      |
    |         6 |  10006 | F      |
    |         7 |  10007 | F      |
    |         8 |  10008 | M      |
    |         9 |  10009 | F      |
    |        10 |  10010 | F      |
    +-----------+--------+--------+
    10 rows in set (0.00 sec)
    
    --
    -- 方法二 (推荐)
    --
    (gcdb@localhost) 11:42:31 [employees]> select @rn1:=@rn1+1 as rownumber, emp_no, gender from employees, (select @rn1:=0) as a limit 10;
    +-----------+--------+--------+
    | rownumber | emp_no | gender |
    +-----------+--------+--------+
    |         1 |  10001 | M      |
    |         2 |  10002 | F      |
    |         3 |  10003 | M      |
    |         4 |  10004 | M      |
    |         5 |  10005 | M      |
    |         6 |  10006 | F      |
    |         7 |  10007 | F      |
    |         8 |  10008 | M      |
    |         9 |  10009 | F      |
    |        10 |  10010 | F      |
    +-----------+--------+--------+
    10 rows in set (0.00 sec)
    
    -- MySQL 自定义变量,根据每一记录进行变化的
    
    (gcdb@localhost) 11:44:55 [employees]> select @rn1:=0;
    +---------+
    | @rn1:=0 |
    +---------+
    |       0 |  -- 只有一行记录
    +---------+
    1 row in set (0.00 sec)
    
    -- 相当于 把 employees 和 (select @rn1:=0)做了笛卡尔积,然后使用@rn1:=@rn + 1,根据每行进行累加
    
    --
    -- ":=" 和 "="
    --
    (gcdb@localhost) 11:42:15 [employees]> set @rn:=0;  -- 赋值为0
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:44:55 [employees]> select @rn1:=0;
    +---------+
    | @rn1:=0 |
    +---------+
    |       0 |
    +---------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 11:46:37 [employees]> set @a:=100;    -- 赋值为100
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:46:54 [employees]> select @a;
    +------+
    | @a   |
    +------+
    |  100 |
    +------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 11:49:25 [employees]> select @a=99;  -- 进行比较
    +-------+
    | @a=99 |
    +-------+
    |     0 |
    +-------+
    1 row in set (0.00 sec)
    
    • 使用子查询实现RowNumber
    1. 思路

      • 假设当前在第N行记录,通过主键emp_no遍历有多少行的记录小于等于当前行,即为当前行的行数
    2. SQL语句

    (gcdb@localhost) 12:07:39 [employees]> SELECT (SELECT COUNT(1) FROM employees b WHERE b.emp_no <= a.emp_no ) AS row_number, emp_no,CONCAT(last_name," ",first_name) name,gender,hire_date FROM employees a ORDER BY emp_no LIMIT 10;
    +------------+--------+--------------------+--------+------------+
    | row_number | emp_no | name               | gender | hire_date  |
    +------------+--------+--------------------+--------+------------+
    |          1 |  10001 | Facello Georgi     | M      | 1986-06-26 |
    |          2 |  10002 | Simmel Bezalel     | F      | 1985-11-21 |
    |          3 |  10003 | Bamford Parto      | M      | 1986-08-28 |
    |          4 |  10004 | Koblick Chirstian  | M      | 1986-12-01 |
    |          5 |  10005 | Maliniak Kyoichi   | M      | 1989-09-12 |
    |          6 |  10006 | Preusig Anneke     | F      | 1989-06-02 |
    |          7 |  10007 | Zielinski Tzvetan  | F      | 1989-02-10 |
    |          8 |  10008 | Kalloufi Saniya    | M      | 1994-09-15 |
    |          9 |  10009 | Peac Sumant        | F      | 1985-02-18 |
    |         10 |  10010 | Piveteau Duangkaew | F      | 1989-08-24 |
    +------------+--------+--------------------+--------+------------+
    10 rows in set (0.59 sec)
    
    
    -- 假设当前在第5行
    (gcdb@localhost) 12:08:38 [employees]> select  b.emp_no  from employees.employees as b order by b.emp_no limit 5;
    +--------+
    | emp_no |
    +--------+
    |  10001 |
    |  10002 |
    |  10003 |
    |  10004 |
    |  10005 |  -- 第5行的emp_no是10005
    +--------+
    5 rows in set (0.00 sec)
    
    (gcdb@localhost) 12:10:28 [employees]> select  count(*)  from employees.employees as b where b.emp_no<= 10005 order by b.emp_no;
    --查找小于等于5的行数有几行
    +----------+
    | count(*) |
    +----------+
    |        5 |  -- 小于等于10005的记录有5行,则5就是10005该行记录的行号
    +----------+
    1 row in set (0.00 sec)
    
    -- 将该子查询的结果即可作为RowNumber,子查询循环多次,不推荐使用。
    
    -- 推荐使用下面这种方法
    (gcdb@localhost) 12:12:01 [employees]> SELECT @a:=@a+1 AS row_number,emp_no,CONCAT(last_name," ",first_name) name,gender,hire_date FROM employees,(SELECT @a:=0) AS a LIMIT 10;
    +------------+--------+--------------------+--------+------------+
    | row_number | emp_no | name               | gender | hire_date  |
    +------------+--------+--------------------+--------+------------+
    |          1 |  10001 | Facello Georgi     | M      | 1986-06-26 |
    |          2 |  10002 | Simmel Bezalel     | F      | 1985-11-21 |
    |          3 |  10003 | Bamford Parto      | M      | 1986-08-28 |
    |          4 |  10004 | Koblick Chirstian  | M      | 1986-12-01 |
    |          5 |  10005 | Maliniak Kyoichi   | M      | 1989-09-12 |
    |          6 |  10006 | Preusig Anneke     | F      | 1989-06-02 |
    |          7 |  10007 | Zielinski Tzvetan  | F      | 1989-02-10 |
    |          8 |  10008 | Kalloufi Saniya    | M      | 1994-09-15 |
    |          9 |  10009 | Peac Sumant        | F      | 1985-02-18 |
    |         10 |  10010 | Piveteau Duangkaew | F      | 1989-08-24 |
    +------------+--------+--------------------+--------+------------+
    10 rows in set (0.00 sec)
    
    

    3.查询employees表下基层用户的最近详细信息(员工号,员工名字,职位,部门,工资)

    关于Group By在《SQL必知必会》中提及的部分规定:

    GROUP BY子句中列出的每一列都必须是检索列有效的表达式(但不能是聚集函数),如果在SELECT中使用表达式,则必须在GROUP BY子句中指定相同的表达式不能使用别名

    除聚集计算语句外,SELECT语句中的每一列都必须在GROUP BY子句中给出

    SELECT 
        e.emp_no AS 员工号,
        CONCAT(last_name, ' ', first_name) AS 姓名,
        t.title AS 职位,
        dp.dept_name AS 部门,
        s.salary AS 工资
    FROM
        employees e
            LEFT JOIN
        dept_manager d ON e.emp_no = d.emp_no
            LEFT JOIN
        (SELECT 
            emp_no, title, from_date, to_date
        FROM
            titles
        WHERE
            (emp_no , from_date, to_date) IN (SELECT 
                    emp_no, MAX(from_date), MAX(to_date)
                FROM
                    titles AS b
                GROUP BY b.emp_no)) t ON t.emp_no = e.emp_no
            LEFT JOIN
        (SELECT 
            dept_no, emp_no, from_date, to_date
        FROM
            dept_emp
        WHERE
            (emp_no , from_date, to_date) IN (SELECT 
                    emp_no, MAX(from_date), MAX(to_date)
                FROM
                    dept_emp AS b
                GROUP BY b.emp_no)) de ON de.emp_no = e.emp_no
            LEFT JOIN
        (SELECT 
            emp_no, salary, from_date, to_date
        FROM
            salaries
        WHERE
            (emp_no , from_date, to_date) IN (SELECT 
                    emp_no, MAX(from_date), MAX(to_date)
                FROM
                    salaries AS b
                GROUP BY b.emp_no)) s ON s.emp_no = e.emp_no
            LEFT JOIN
        departments dp ON dp.dept_no = de.dept_no
    WHERE
        d.emp_no IS NULL
    LIMIT 10;
    
    --
    -- 改进的子查询语句 - 1
    --
    SELECT 
        emp_no, title, from_date, to_date
    FROM
        titles
            WHERE
            (emp_no , from_date, to_date) IN 
                (
                    SELECT 
                        emp_no, MAX(from_date), MAX(to_date)  -- 因为数据本身的问题,这里from_date和to_date都要
                    FROM
                        titles AS b
                        GROUP BY b.emp_no
                ) -- 这个子查询表示以emp_no分类,找到最大(最近)的from_date和to_date
                  -- 而where条件在这个最大的基础上,过滤出我们要的title。(salary同理)
    
    
    --
    -- 改进的子查询语句 - 2
    --
    SELECT 
        emp_no, title, from_date, to_date
    FROM
        titles AS a
    WHERE
        (from_date, to_date) = (SELECT 
                MAX(from_date), MAX(to_date)  -- 同样使用from_date和to_date
            FROM
                titles AS b
            WHERE
                a.emp_no = b.emp_no  -- 这个是一个关联子查询
            GROUP BY b.emp_no);
    
  • 相关阅读:
    北京高考零分作文(看到最后一句笑喷了!)
    关于前几天无法访问的问题
    用 PHP 读取和编写 XML DOM[转]
    Delphi对INI文件的详细操作方法
    如何在WebService中获取客户端的IP地址
    正则表达式30分钟入门教程
    [原创]shell对xml操作的脚本
    预防SQL注入攻击之我见(好文章)
    表驱动方法(非常好的数据结构)
    请教shell读写XML问题
  • 原文地址:https://www.cnblogs.com/gczheng/p/8004761.html
Copyright © 2011-2022 走看看