Oracle开发者中级第3课(如何对行进行排序)实验

Posted dingdingfish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle开发者中级第3课(如何对行进行排序)实验相关的知识,希望对你有一定的参考价值。

概述

本实验参考DevGym中的实验指南

创建环境

首先创建表:

create table toys (
  toy_name       varchar2(30),
  weight         integer,
  price          number(5,2),
  purchased_date date,
  last_lost_date date
);

insert into toys values ('Miss Snuggles', 4,  9.99,  date'2018-02-01', date'2018-06-01');
insert into toys values ('Baby Turtle',   1,  5.00,  date'2016-09-01', date'2017-03-03');
insert into toys values ('Kangaroo',      10, 29.99, date'2017-03-01', date'2018-06-01');
insert into toys values ('Blue Dinosaur', 8,  9.99,  date'2013-07-01', date'2016-11-01');
insert into toys values ('Purple Ninja',  8,  29.99, date'2018-02-01', null);

commit;

标准排序

查询数据如下,目前看是和插入顺序一致的,但这并不能保证:

SQL> select * from toys;

        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE
________________ _________ ________ _________________ _________________
Miss Snuggles            4     9.99 01-FEB-18         01-JUN-18
Baby Turtle              1        5 01-SEP-16         03-MAR-17
Kangaroo                10    29.99 01-MAR-17         01-JUN-18
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16
Purple Ninja             8    29.99 01-FEB-18

可以对数据进行排序,默认为升序:

SQL> select * from toys order by price;
        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE
________________ _________ ________ _________________ _________________
Baby Turtle              1        5 01-SEP-16         03-MAR-17
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16
Miss Snuggles            4     9.99 01-FEB-18         01-JUN-18
Purple Ninja             8    29.99 01-FEB-18
Kangaroo                10    29.99 01-MAR-17         01-JUN-18

也可以降序:

SQL> select * from toys order by price desc;
        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE
________________ _________ ________ _________________ _________________
Kangaroo                10    29.99 01-MAR-17         01-JUN-18
Purple Ninja             8    29.99 01-FEB-18
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16
Miss Snuggles            4     9.99 01-FEB-18         01-JUN-18
Baby Turtle              1        5 01-SEP-16         03-MAR-17

注意到有两个玩具的价格都是9.99,因此数据库返回这两个玩具时,它们的先后顺序并不能保证。

不过在表的物理结构不变的情况下,这种现象是不会出现的。因此我们先删除这两条数据,然后以与最初不同的顺序插入。此时即可出现不同的排序结果:

delete from toys where price = 9.99;
insert into toys values ('Blue Dinosaur', 8,  9.99,  date'2013-07-01', date'2016-11-01');
insert into toys values ('Miss Snuggles', 4,  9.99,  date'2018-02-01', date'2018-06-01');
select * from toys order by price;

        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE
________________ _________ ________ _________________ _________________
...
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16
Purple Ninja             8    29.99 01-FEB-18
...

原文中使用了Resolving Ties,也可以理解为Break Ties,意为解决平局或打破僵局。此时需要在order by中新增一列。

SQL> select toy_name, price from toys order  by price, toy_name;

        TOY_NAME    PRICE
________________ ________
Baby Turtle             5
Blue Dinosaur        9.99
Miss Snuggles        9.99
Kangaroo            29.99
Purple Ninja        29.99

空值的处理

再来看下空值,默认排序时空值是放在最后的:

SQL> select * from toys order  by last_lost_date;

        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE
________________ _________ ________ _________________ _________________
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16
Baby Turtle              1        5 01-SEP-16         03-MAR-17
Kangaroo                10    29.99 01-MAR-17         01-JUN-18
Miss Snuggles            4     9.99 01-FEB-18         01-JUN-18
Purple Ninja             8    29.99 01-FEB-18

也可以使用nulls first将空值置于最前:

SQL> select toy_name, price, last_lost_date from toys order by price, last_lost_date nulls first;

        TOY_NAME    PRICE    LAST_LOST_DATE
________________ ________ _________________
Baby Turtle             5 03-MAR-17
Blue Dinosaur        9.99 01-NOV-16
Miss Snuggles        9.99 01-JUN-18
Purple Ninja        29.99
Kangaroo            29.99 01-JUN-18

定制排序

定制排序基于定制列,例如可以将最喜欢的玩具放在最前。这是和case语句结合的,同时又对toy_name进行排序保证了随后的顺序:

SQL> select * from toys
  2  order  by case
  3    when toy_name = 'Miss Snuggles' then 1
  4    else 2
  5  end, toy_name;

        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE
________________ _________ ________ _________________ _________________
Miss Snuggles            4     9.99 01-FEB-18         01-JUN-18
Baby Turtle              1        5 01-SEP-16         03-MAR-17
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16
Kangaroo                10    29.99 01-MAR-17         01-JUN-18
Purple Ninja             8    29.99 01-FEB-18

用别名(不建议用位置符号),语句就更易懂了:

SQL> select t.*,
  2         case
  3           when toy_name = 'Miss Snuggles' then 1
  4           else 2
  5         end custom_sort
  6  from   toys t
  7  order  by custom_sort, toy_name;

        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE    CUSTOM_SORT
________________ _________ ________ _________________ _________________ ______________
Miss Snuggles            4     9.99 01-FEB-18         01-JUN-18                      1
Baby Turtle              1        5 01-SEP-16         03-MAR-17                      2
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16                      2
Kangaroo                10    29.99 01-MAR-17         01-JUN-18                      2
Purple Ninja             8    29.99 01-FEB-18                                        2

然后Module 10的答案如下:

select t.toy_name, t.price,
       case
         when toy_name = 'Kangaroo' then 1
         when toy_name = 'Blue Dinosaur' then 2
         else 3
       end custom_sort
from   toys t
order  by custom_sort, price;

Top-N排序

先来看一个常见的错误。以下是先通过rownum获取前3行,然后再排序:

SQL> select * from toys where rownum <= 3 order  by price desc;

        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE
________________ _________ ________ _________________ _________________
Kangaroo                10    29.99 01-MAR-17         01-JUN-18
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16
Baby Turtle              1        5 01-SEP-16         03-MAR-17

正确的写法是这样的,需要先排序再获取前3行:

select * from (
  select *
  from   toys t
  order  by price desc
)
where  rownum <= 3;

也可以结合函数row_number来实现:

select * from (
  select t.*, row_number() over (order by price desc) rn
  from   toys t
)
where  rn <= 3
order  by rn;

看一下中间子查询的结果就明白了:

SQL> select t.*, row_number() over (order by price desc) rn from toys t;

        TOY_NAME    WEIGHT    PRICE    PURCHASED_DATE    LAST_LOST_DATE    RN
________________ _________ ________ _________________ _________________ _____
Kangaroo                10    29.99 01-MAR-17         01-JUN-18             1
Purple Ninja             8    29.99 01-FEB-18                               2
Miss Snuggles            4     9.99 01-FEB-18         01-JUN-18             3
Blue Dinosaur            8     9.99 01-JUL-13         01-NOV-16             4
Baby Turtle              1        5 01-SEP-16         03-MAR-17             5

Oracle 12c后,可以使用行限制子句,就更简单了:

select * from toys
order  by price desc
fetch  first 3 rows only;

row_limiting_clause的更多示例参见这里

Top-N排序仍然要解决Tie的问题,例如工资前5位,如果有工资一样的,那结果就不一定只有5个人。

这就取决于你想要什么结果了。row_limiting_clause中有with ties子句,可以明确这个问题:

SQL> select toy_name, price from toys
  2  order  by price desc
  3  fetch  first 3 rows with ties;

        TOY_NAME    PRICE
________________ ________
Kangaroo            29.99
Purple Ninja        29.99
Blue Dinosaur        9.99
Miss Snuggles        9.99

对比下面的查询:

SQL> select toy_name, price from toys
  2  order  by price desc
  3  fetch  first 3 rows only;

        TOY_NAME    PRICE
________________ ________
Kangaroo            29.99
Purple Ninja        29.99
Blue Dinosaur        9.99

如果用子查询实现,可以结合rank实现,看一下以下的例句就明白了:

SQL> select price, rank() over ( order by price desc ) rn from toys t;

   PRICE    RN
________ _____
   29.99     1
   29.99     1
    9.99     3
    9.99     3
       5     5

SQL> select price, dense_rank() over ( order by price desc ) rn from toys t;

   PRICE    RN
________ _____
   29.99     1
   29.99     1
    9.99     2
    9.99     2
       5     3

with ties的真正含义如下,注意其只多返回和最后一行的列值相同的行:

Specify WITH TIES to return additional rows with the same sort key as the last row fetched.

以上是关于Oracle开发者中级第3课(如何对行进行排序)实验的主要内容,如果未能解决你的问题,请参考以下文章

Oracle开发者中级第4课(分析函数)实验

Oracle开发者中级第8课(Merge)实验

Oracle开发者中级第8课(Merge)实验

Oracle开发者中级第1课(Null)实验

Oracle开发者中级第7课(层级查询)实验

Oracle开发者中级第7课(层级查询)实验