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课(如何对行进行排序)实验的主要内容,如果未能解决你的问题,请参考以下文章