博客 SQL开发实战系列(三):SQL排序的那些事

SQL开发实战系列(三):SQL排序的那些事

   数栈君   发表于 2023-02-27 18:28  218  0

一、以指定的次序返回查询结果
实际提取数据或生成报表时,一般都要根据一定的顺序查看,比如,想查看单位所雇员工的信息。

SELECT empno, ename, hiredate
FROM emp
WHERE deptno = 10
ORDER BY hiredate ASC;
EMPNO ENAME HIREDATE
7782 CLARK 1981-6-9
7839 KING 1981-11-17
7934 MILLER 1982-1-23

这种语句很多人都会写,但除了ORDER BY hiredate ASC这种写法外,还可以写成ORDER BY 3 ASC,意思是按第三列排序。

SELECT empno, ename, hiredate
FROM emp
WHERE deptno = 10
ORDER BY 3 ASC;
EMPNO ENAME HIREDATE
7782 CLARK 1981-6-9
7839 KING 1981-11-17
7934 MILLER 1982-1-23

当取值不定时,用这种方法就很方便,比如,有时取sal,有时要取comm来显示:

SQL>
SQL> SELECT empno, ename, sal
2 FROM emp
3 WHERE deptno = 10
4 ORDER BY 3 ASC;

EMPNO ENAME SAL
----- ---------- ---------
7934 MILLER 1300.00
7782 CLARK 2450.00
7839 KING 5000.00

SQL>
SQL> SELECT empno, ename, comm
2 FROM emp
3 WHERE deptno = 10
4 ORDER BY 3 ASC;

EMPNO ENAME COMM
----- ---------- ---------
7782 CLARK
7934 MILLER
7839 KING

SQL>


对于这种需求,如果order by后使用列名,就需要注意前后保待一致,否则会给java开发人员带来一些麻烦。比如,开发初期的语句如下:

String str=null;
str=str+"select ename,hiredate,sal"
str=str+"from emp"
str=str+"order by ename"

后来要求增加empno的显示及排序,而我们经常要按第一列排序,代码需要改为:

String str=null;
str=str+"select empno,ename,hiredate,sal"
str=str+"from emp"
str=str+"order by empno"

如果语句比较复杂,会经常忘记更改后面的order by,但使用orderby 1这种方式就没问题。
需要注意的是,用数据来代替列位置只能用于order by子句中,其他地方都不能用。

二、按多个字段排序
如果按多列排序且有升有降怎么办?如:按部门编号升序,并按工资降序排列。排序时有两个关键字:ASC表示升序、DESC表示降序。
所以我们在order by后加两列,并分别标明ASC、DESC。

SQL> SELECT empno,deptno,sal,ename,job FROM emp ORDER BY 2 ASC, 3 DESC;

EMPNO DEPTNO SAL ENAME JOB
----- ------ --------- ---------- ---------
7839 10 5000.00 KING PRESIDENT
7782 10 2450.00 CLARK MANAGER
7934 10 1300.00 MILLER CLERK
7788 20 3000.00 SCOTT ANALYST
7902 20 3000.00 FORD ANALYST
7566 20 2975.00 JONES MANAGER
7876 20 1100.00 ADAMS CLERK
7369 20 800.00 SMITH CLERK
7698 30 2850.00 BLAKE MANAGER
7499 30 1600.00 ALLEN SALESMAN
7844 30 1500.00 TURNER SALESMAN
7521 30 1250.00 WARD SALESMAN
7654 30 1250.00 MARTIN SALESMAN
7900 30 950.00 JAMES CLERK
1001 test

15 rows selected


多列排序时,若前面的列有重复值(如deptno=10有3行数据),后面的排序才有用。相当于是通过前面的列把数据分成了几组,然后每组的数据再按后面的列进行排序。

三、按子串排序
有一种速查法就是按顾客电话号码尾号的顺序记录,这样在查找的时候就可以快速缩小查询范围,增强顾客的认可度。如果要按这种方法排序,应该怎么做呢?通过函数取出后面几位所需的信息即可。

with t as (
select 'zyd' as ename ,'18710059586' as phone from dual
union all
select 'zyd1','18710059386' as phone from dual
union all
select 'zyd2','18710059986' as phone from dual
)
select ename,phone,substr(phone,-4) as 尾号
from t
order by 3

由此可见:只要能将数据查询出来,就能根据相应的信息排序。

四、TRANSLATE
语法格式:TRANSLATE(expr,from_string,to_string)
案例如下

select translate('zhaoyandong','yand','@#$%') from dual
TRANSLATE('ZHAOYANDONG','YAND','@#$%')
zh#o@#$%o$g

from_string与to_string以字符为单位,对应字符一一替换。
如果to_string为空,则返回空值。

SQL> select translate('zhaoyandong','yand','') from dual;

TRANSLATE('ZHAOYANDONG','YAND','')
----------------------------------------------------------------

SQL>

如果to_string对应的位置没有字符,删除from_string中列出的字符将会被消掉。

SQL>
SQL> select translate('zhaoyandong','yand','ya') from dual;

TRANSLATE('ZHAOYANDONG','YAND','YA')
----------------------------------------------------------------
zhaoyaog

五、按数字和字母混合字符串中的字母排序
创建案例数据临时表:

with t as (
select empno||ename as vname from emp
)
select * from t
VNAME
7369SMITH
7499ALLEN
7521WARD
7566JONES
7654MARTIN
7698BLAKE
7782CLARK
7788SCOTT
7839KING
7844TURNER
7876ADAMS
7900JAMES
7902FORD
7934MILLER
1001test


这个需求就难一点了,看到里面的字母(也就是原来的列ename)吗?要求按其中的字母(列ename)排序。
那么就要先取出其中的字母才行,我们可以用translate的替换功能,把数字与空格都替换为空:

with t as (
select empno||ename as vname from emp
)
select t.vname,translate(vname,'-0123456789','-') as tmp from t
order by 2
VNAME TMP
-------------------------------------------------- --------------------------------------------------------------------------------
7876ADAMS ADAMS
7499ALLEN ALLEN
7698BLAKE BLAKE
7782CLARK CLARK
7902FORD FORD
7900JAMES JAMES
7566JONES JONES
7839KING KING
7654MARTIN MARTIN
7934MILLER MILLER
7788SCOTT SCOTT
7369SMITH SMITH
7844TURNER TURNER
7521WARD WARD
1001test test

15 rows selected


其实还可以通过正则regexp_replace等等方式处理,后面文章会写出来~!

六、处理排序空值
Oracle默认排序空值在后面,如果想把空值(如emp.comm)显示在前面怎么办,用NVL(comm,-1)吗?
也许很多人都是用的这种方法,但这种方法需要对列类型及其中保存的数据有所了解才行,而且保存的数据如果有变化,该语句就要重新维护。
其实可以用关键字NULLS FIRST和NULLS LAST。空值在前写法:

select * from emp order by comm nulls first;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
7369 SMITH CLERK 7902 1980-12-17 800.00 20
7788 SCOTT ANALYST 7566 1987-4-19 3000.00 20
7934 MILLER CLERK 7782 1982-1-23 1300.00 10
7902 FORD ANALYST 7566 1981-12-3 3000.00 20
7900 JAMES CLERK 7698 1981-12-3 950.00 30
7566 JONES MANAGER 7839 1981-4-2 2975.00 20
7698 BLAKE MANAGER 7839 1981-5-1 2850.00 30
7782 CLARK MANAGER 7839 1981-6-9 2450.00 10
1001 test 2021-10-9 1
7839 KING PRESIDENT 1981-11-17 5000.00 10
7876 ADAMS CLERK 7788 1987-5-23 1100.00 20
7844 TURNER SALESMAN 7698 1981-9-8 1500.00 0.00 30
7499 ALLEN SALESMAN 7698 1981-2-20 1600.00 300.00 30
7521 WARD SALESMAN 7698 1981-2-22 1250.00 500.00 30
7654 MARTIN SALESMAN 7698 1981-9-28 1250.00 1400.00 30

15 rows selected


空值在后写法:

SQL> select * from emp order by comm nulls last;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
7844 TURNER SALESMAN 7698 1981-9-8 1500.00 0.00 30
7499 ALLEN SALESMAN 7698 1981-2-20 1600.00 300.00 30
7521 WARD SALESMAN 7698 1981-2-22 1250.00 500.00 30
7654 MARTIN SALESMAN 7698 1981-9-28 1250.00 1400.00 30
1001 test 2021-10-9 1
7839 KING PRESIDENT 1981-11-17 5000.00 10
7876 ADAMS CLERK 7788 1987-5-23 1100.00 20
7900 JAMES CLERK 7698 1981-12-3 950.00 30
7902 FORD ANALYST 7566 1981-12-3 3000.00 20
7934 MILLER CLERK 7782 1982-1-23 1300.00 10
7782 CLARK MANAGER 7839 1981-6-9 2450.00 10
7698 BLAKE MANAGER 7839 1981-5-1 2850.00 30
7566 JONES MANAGER 7839 1981-4-2 2975.00 20
7369 SMITH CLERK 7902 1980-12-17 800.00 20
7788 SCOTT ANALYST 7566 1987-4-19 3000.00 20

15 rows selected


这样写方便的多!

七、根据条件取不同列中的值来排序
有时排序的要求会比较复杂,比如:领导对工资在1000到2000元之间的员工更感兴趣,于是要求工资在这个范围的员工要排在前面,以便优先查看。
对于这种需求,我们可以在查询中新生成一列,用多列排序的方法处理:

SELECT empno AS 编码,
ename AS 姓名,
CASE
WHEN sal>= 1000 AND sal < 2000 THEN

ELSE

END AS 级别,
sal AS 工资
FROM emp
WHERE deptno = 30
ORDER BY 3, 4;
编码 姓名 级别 工资
----- ---------- ---------- ---------
7654 MARTIN 1 1250.00
7521 WARD 1 1250.00
7844 TURNER 1 1500.00
7499 ALLEN 1 1600.00
7900 JAMES 2 950.00
7698 BLAKE 2 2850.00

6 rows selected


可以看到,950与2850都排在了后面,也可以不显示级别,直接把case when放在
order by中:

SELECT empno AS 编码,
ename AS 姓名,
sal AS 工资
FROM emp
WHERE deptno = 30
ORDER BY ( CASE
WHEN sal>= 1000 AND sal < 2000 THEN
1
ELSE
2
END),3;
编码 姓名 工资
----- ---------- ---------
7654 MARTIN 1250.00
7521 WARD 1250.00
7844 TURNER 1500.00
7499 ALLEN 1600.00
7900 JAMES 950.00
7698 BLAKE 2850.00

6 rows selected


————————————————

免责申明:


本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!

想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs
同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:https://github.com/DTStack

0条评论
社区公告
  • 大数据领域最专业的产品&技术交流社区,专注于探讨与分享大数据领域有趣又火热的信息,专业又专注的数据人园地

最新活动更多
微信扫码获取数字化转型资料
钉钉扫码加入技术交流群