SQL对查询结果进⾏排序(ORDERBY)
⽬录
学习重点
使⽤ORDER BY⼦句对查询结果进⾏排序。
在ORDER BY⼦句中列名的后⾯使⽤关键字ASC可以进⾏升序排序,使⽤DESC关键字可以进⾏降序排序。
ORDER BY⼦句中可以指定多个排序键。
排序健中包含NULL时,会在开头或末尾进⾏汇总。
ORDER BY⼦句中可以使⽤SELECT⼦句中定义的列的别名。
ORDER BY⼦句中可以使⽤SELECT⼦句中未出现的列或者聚合函数。
ORDER BY⼦句中不能使⽤列的编号。
⼀、ORDER BY⼦句
截⾄⽬前,我们使⽤了各种各样的条件对表中的数据进⾏查询。本节让我们再来回顾⼀下简单的SELECT语句(代码清单 27)。
代码清单 27 显⽰商品编号、商品名称、销售单价和进货单价的SELECT语句
SELECT product_id, product_name, sale_price, purchase_price
FROM Product;
执⾏结果
product_id | product_name | sale_price | purchase_price
------------+---------------+--------------+----------------
0001 | T恤衫 | 1000 | 500
0002 | 打孔器 | 500 | 320
0003 | 运动T恤 | 4000 | 2800
0004 | 菜⼑ | 3000 | 2800
0005 | ⾼压锅 | 6800 | 5000
0006 | 叉⼦ | 500 |
0007 | 擦菜板 | 880 | 790
0008 | 圆珠笔 | 100 |
对于上述结果,在此⽆需特别说明,本节要为⼤家介绍的不是查询结果,⽽是查询结果的排列顺序。
那么,结果中的 8 ⾏记录到底是按照什么顺序排列的呢?乍⼀看,貌似是按照商品编号从⼩到⼤的顺序(升序)排列的。其实,排列顺序是随机的,这只是个偶然。因此,再次执⾏同⼀条SELECT语句时,顺序可能⼤为不同。
KEYWORD
升序
通常,从表中抽取数据时,如果没有特别指定顺序,最终排列顺序便⽆从得知。即使是同⼀条SELECT语句,每次执⾏时排列顺序很可能发⽣改变。
但是不进⾏排序,很可能出现结果混乱的情况。这时,便需要通过在SELECT语句末尾添加ORDER BY⼦句来明确指定排列顺序。
KEYWORD
ORDER BY⼦句
ORDER BY⼦句的语法如下所⽰。
语法 4 ORDER BY⼦句
SELECT <;列名1>, <;列名2>, <;列名3>, ……
FROM <;表名>
ORDER BY <;排序基准列1>, <;排序基准列2>, ……
例如,按照销售单价由低到⾼,也就是升序排列时,请参见代码清单 28。
代码清单 28 按照销售单价由低到⾼(升序)进⾏排列
SELECT product_id, product_name, sale_price, purchase_price
FROM Product
ORDER BY sale_price;
执⾏结果
不论何种情况,ORDER BY⼦句都需要写在SELECT语句的末尾。这是因为对数据⾏进⾏排序的操作必须在结果即将返回时执⾏。ORDER BY ⼦句中书写的列名称为排序键。该⼦句与其他⼦句的顺序关系如下所⽰。
KEYWORD
排序键
▶⼦句的书写顺序
1. SELECT⼦句→
2. FROM⼦句→
3. WHERE⼦句→
4. GROUP BY⼦句→
5. HAVING⼦句→
6. ORDER BY⼦句
法则 15
ORDER BY⼦句通常写在 SELECT 语句的末尾。
不想指定数据⾏的排列顺序时,SELECT语句中不写ORDER BY⼦句也没关系。
⼆、指定升序或降序
与上述⽰例相反,想要按照销售单价由⾼到低,也就是降序排列时,可以参见代码清单 29,在列名后⾯使⽤DESC关键字。
KEYWORD
降序
DESC关键字
代码清单 29 按照销售单价由⾼到低(降序)进⾏排列
SELECT product_id, product_name, sale_price, purchase_price
FROM Product
ORDER BY sale_price DESC;
执⾏结果
product_id | product_name | sale_ price | purchase_ price
------------+--------------+-------------+----------------
0005 | ⾼压锅 | 6800 | 5000
0003 | 运动T恤 | 4000 | 2800
0004 | 菜⼑ | 3000 | 2800
0001 | T恤衫 | 1000 | 500
0007 | 擦菜板 | 880 | 790
0002 | 打孔器 | 500 | 320
0006 | 叉⼦ | 500 |
0008 | 圆珠笔 | 100 |
如上所⽰,这次销售单价最⾼(6800 ⽇元)的⾼压锅排在了第⼀位。其实,使⽤升序进⾏排列时,正式的书写⽅式应该是使⽤关键字ASC,但是省略该关键字时会默认使⽤升序进⾏排序。这可能是因为实际应⽤中按照升序排序的情况更多吧。ASC和DESC是 ascendent(上
升的)和 descendent(下降的)这两个单词的缩写。
KEYWORD
ASC关键字
法则 16
未指定ORDER BY⼦句中排列顺序时会默认使⽤升序进⾏排列。
由于ASC和DESC这两个关键字是以列为单位指定的,因此可以同时指定⼀个列为升序,指定其他列为降序。
三、指定多个排序键
本节开头曾提到过对销售单价进⾏升序排列的SELECT语句(代码清单 28)的执⾏结果,我们再来回顾⼀下。可以发现销售单价为 500 ⽇元的商品有 2 件。相同价格的商品的顺序并没有特别指定,或者可以说是随机排列的。
如果想要对该顺序的商品进⾏更细致的排序的话,就需要再添加⼀个排序键。在此,我们以添加商品编号的升序为例,请参见代码清单30。
代码清单 30 按照销售单价和商品编号的升序进⾏排序
SELECT product_id, product_name, sale_price, purchase_price
FROM Product
ORDER BY sale_price, product_id;
执⾏结果
这样⼀来,就可以在ORDER BY⼦句中同时指定多个排序键了。规则是优先使⽤左侧的键,如果该列存在相同值的话,再接着参考右侧的键。当然,也可以同时使⽤ 3 个以上的排序键。
四、NULL的顺序
在此前的⽰例中,我们已经使⽤过销售单价(sale_price列)作为排序键了,这次让我们尝试使⽤进货单价(purchase_price列)作为排序键吧。此时,问题来了,圆珠笔和叉⼦对应的值是NULL,究竟NULL会按照什么顺序进⾏排列呢?NULL是⼤于 100 还是⼩于 100 呢?或者说5000 和NULL哪个更⼤呢?
请⼤家回忆⼀下我们在中学过的内容。没错,不能对NULL使⽤⽐较运算符,也就是说,不能对NULL和数字进⾏排序,也不能与字符串和⽇期⽐较⼤⼩。因此,使⽤含有NULL的列作为排序键时,NULL会在结果的开头或末尾汇总显⽰(代码清单 31)。
代码清单 31 按照进货单价的升序进⾏排列
SELECT product_id, product_name, sale_price, purchase_price
FROM Product
ORDER BY purchase_price;
执⾏结果
究竟是在开头显⽰还是在末尾显⽰,并没有特殊规定。某些 DBMS 中可以指定NULL在开头或末尾显⽰,希望⼤家对⾃⼰使⽤的 DBMS 的功能研究⼀下。
法则 17
排序键中包含NULL时,会在开头或末尾进⾏汇总。
五、在排序键中使⽤显⽰⽤的别名
在中“常见错误②”中曾介绍过,在GROUP BY⼦句中不能使⽤SELECT⼦句中定义的别名,但是在ORDER BY⼦句中却是允许使⽤别名的。因此,代码清单 32 中的SELECT语句并不会出错,可正确执⾏。
代码清单 32 ORDER BY⼦句中可以使⽤列的别名
SELECT product_id AS id, product_name, sale_price AS sp, purchase_price
FROM Product
ORDER BY sp, id;
上述SELECT语句与之前按照“销售单价和商品编号的升序进⾏排列”的SELECT语句(代码清单 31)意思完全相同。
执⾏结果
id | product_name | sp | purchase_price
------+---------------+-------+---------------
0008 | 圆珠笔 | 100 |
0002 | 打孔器 | 500 | 320
0006 | 叉⼦ | 500 |
0007 | 擦菜板 | 880 | 790
0001 | T恤衫 | 1000 | 500
0004 | 菜⼑ | 3000 | 2800
0003 | 运动T恤 | 4000 | 2800
0005 | ⾼压锅 | 6800 | 5000
不能在GROUP BY⼦句中使⽤的别名,为什么可以在ORDER BY⼦句中使⽤呢?这是因为 SQL 语句在 DBMS 内部的执⾏顺序被掩盖起来了。SELECT语句按照⼦句为单位的执⾏顺序如下所⽰。
▶使⽤HAVING⼦句时SELECT语句的顺序
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY
这只是⼀个粗略的总结,虽然具体的执⾏顺序根据 DBMS 的不同⽽不同,但是⼤家有这样⼀个⼤致的印象就可以了。⼀定要记住SELECT⼦句的执⾏顺序在GROUP BY⼦句之后,ORDER BY⼦句之前。因此,在执⾏GROUP BY⼦句时,SELECT语句中定义的别名⽆法被识别。对于在SELECT⼦句之后执⾏的ORDER BY⼦句来说,就没有这样的问题了。
法则 18
在ORDER BY⼦句中可以使⽤SELECT⼦句中定义的别名。
六、ORDER BY⼦句中可以使⽤的列
ORDER BY⼦句中也可以使⽤存在于表中、但并不包含在SELECT⼦句之中的列(代码清单 33)。
代码清单 33 SELECT⼦句中未包含的列也可以在ORDER BY⼦句中使⽤
SELECT product_name, sale_price, purchase_price
FROM Product
ORDER BY product_id;
执⾏结果
product_name | sale_price | purchase_price
---------------+-------------+----------------
T恤衫 | 1000 | 500
打孔器 | 500 | 320
运动T恤 | 4000 | 2800
菜⼑ | 3000 | 2800
⾼压锅 | 6800 | 5000
叉⼦ | 500 |
擦菜板 | 880 | 790
圆珠笔 | 100 |
除此之外,还可以使⽤聚合函数(代码清单 34)。
代码清单 34 ORDER BY⼦句中也可以使⽤聚合函数
执⾏结果
product_type | count
---------------+------
⾐服 | 2
办公⽤品 | 2
厨房⽤具 | 4
法则 19
在ORDER BY⼦句中可以使⽤SELECT⼦句中未使⽤的列和聚合函数。
七、不要使⽤列编号
在ORDER BY⼦句中,还可以使⽤在SELECT⼦句中出现的列所对应的编号,是不是没想到?列编号是指SELECT⼦句中的列按照从左到右的顺序进⾏排列时所对应的编号(1, 2, 3,…)。因此,代码清单 35 中的两条SELECT语句的含义是相同的。
KEYWORD
列编号
代码清单 35 ORDER BY⼦句中可以使⽤列的编号
sql语句查询不包含-- 通过列名指定
SELECT product_id, product_name, sale_price, purchase_price
FROM Product
ORDER BY sale_price DESC, product_id;
-- 通过列编号指定
SELECT product_id, product_name, sale_price, purchase_price
FROM Product
ORDER BY 3 DESC, 1;
上述第 2 条SELECT语句中的ORDER BY⼦句所代表的含义,就是“按照SELECT⼦句中第 3 列的降序和第 1 列的升序进⾏排列”,这和第 1 条SELECT语句的含义完全相同。
执⾏结果
product_id | product_name | sale_price | purchase_price
-----------+---------------+-------------+----------------
0005 | ⾼压锅 | 6800 | 5000
0003 | 运动T恤 | 4000 | 2800
0004 | 菜⼑ | 3000 | 2800
0001 | T恤衫 | 1000 | 500
0007 | 擦菜板 | 880 | 790
0002 | 打孔器 | 500 | 320
0006 | 叉⼦ | 500 |
0008 | 圆珠笔 | 100 |
虽然列编号使⽤起来⾮常⽅便,但我们并不推荐使⽤,原因有以下两点。
第⼀,代码阅读起来⽐较难。使⽤列编号时,如果只看ORDER BY⼦句是⽆法知道当前是按照哪⼀列进⾏排序的,只能去SELECT⼦句的列表中按照列编号进⾏确认。上述⽰例中SELECT⼦句的列数⽐较少,因此可能并没有什么明显的感觉。但是在实际应⽤中往往会出现列数很多的情况,⽽且SELECT⼦句和ORDER BY⼦句之间,还可能包含很复杂的WHERE⼦句和HAVING⼦句,直接⼈⼯确认实在太⿇烦了。
第⼆,这也是最根本的问题,实际上,在 SQL-92 中已经明确指出该排序功能将来会被删除。因此,虽然现在使⽤起来没有问题,但是将来随着 DBMS 的版本升级,可能原本能够正常执⾏的 SQL 突然就会出错。不光是这种单独使⽤的 SQL 语句,对于那些在系统中混合使⽤的SQL 来说,更要极⼒避免。
法则 20
在ORDER BY⼦句中不要使⽤列编号。
请参阅
(完)
1. 也是因为这⼀原因,HAVING⼦句也不能使⽤别名。
2. 1992 年制定的 SQL 标准。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论