SQL ORDER BY DECODE是将数字排序为字符串?

时间:2017-02-10 03:24:14

标签: sql oracle sorting sql-order-by

我有一张如下创建的航班记录表

CREATE TABLE FLIGHT_DETAILS
(
FLIGHT_ID           NUMBER(10) PRIMARY KEY,
FLIGHT_NO           VARCHAR2(10),
DEPARTURE_DTE       DATE,
TOTAL_PASSENGERS    NUMBER(3)
);

然后我有一个函数,我的应用程序调用它来检索按所选列排序的记录(按降序排列)。

CREATE OR REPLACE FUNCTION func_get_flight_details (

p_order_col IN CHAR )

RETURN sys_refcursor
AS
    v_ref_cursor    sys_refcursor;
    v_sql_str       VARCHAR2(2048);
BEGIN
OPEN v_ref_cursor FOR

SELECT FLIGHT_NO, DEPARTURE_DTE, TOTAL_PASSENGERS 
FROM FLIGHT_DETAILS
ORDER BY DECODE(p_order_col,
        'FLIGHT_NO', FLIGHT_NO,
        'DEPARTURE_DTE', TO_DATE(DEPARTURE_DTE, 'YYYY/MM/DD'),
        'TOTAL_PASSENGERS', TOTAL_PASSENGERS) DESC;

RETURN v_ref_cursor;
END;

FLIGHT_NODEPARTURE_DTE排序正常。当我尝试按TOTAL_PASSENGERS排序时,问题出现了,这让我得到了这个

FLIGHT_NO   DEPARTURE_DTE   TOTAL_PASSENGERS
-------------------------------------------------
OR3237      01/03/16        9
RM7202      15/01/16        50
CQ8429      05/10/16        250
DA5720      21/07/16        100

出于某种原因,DECODE将NUMBER列排序为字符串。为了测试,我尝试了这个

SELECT FLIGHT_NO, DEPARTURE_DTE, TOTAL_PASSENGERS 
FROM FLIGHT_DETAILS
ORDER BY TOTAL_PASSENGERS DESC;

给了我

FLIGHT_NO   DEPARTURE_DTE   TOTAL_PASSENGERS
-------------------------------------------------
CQ8429      05/10/16        250
DA5720      21/07/16        100
RM7202      15/01/16        50
OR3237      01/03/16        9

证明问题不在于列本身。

然后我尝试了一些我在SO上找到的解决方案

ORDER BY DECODE(p_order_col,
        'FLIGHT_NO', FLIGHT_NO,
        'DEPARTURE_DTE', TO_DATE(DEPARTURE_DTE, 'YYYY/MM/DD'),
        'TOTAL_PASSENGERS', TO_NUMBER(TOTAL_PASSENGERS)) DESC;


ORDER BY DECODE(p_order_col,
        'FLIGHT_NO', FLIGHT_NO,
        'DEPARTURE_DTE', TO_DATE(DEPARTURE_DTE, 'YYYY/MM/DD'),
        'TOTAL_PASSENGERS', LPAD(TOTAL_PASSENGERS, 10)) DESC;


ORDER BY DECODE(p_order_col,
        'FLIGHT_NO', FLIGHT_NO,
        'DEPARTURE_DTE', TO_DATE(DEPARTURE_DTE, 'YYYY/MM/DD'),
        'TOTAL_PASSENGERS', TOTAL_PASSENGERS*1) DESC;

没有一个工作(它仍然按字符串排序)。

那么为什么DECODE拒绝将数字列分类为数字呢?我如何才能正确排序?

2 个答案:

答案 0 :(得分:2)

您的问题是decode()是一个表达式,只返回一种类型。因此,必须转换类型。

无论如何你应该使用case。我首选的方法是多个语句:

ORDER BY (CASE WHEN p_order_col = 'FLIGHT_NO' THEN FLIGHT_NO END),
         (CASE WHEN p_order_col = 'DEPARTURE_DTE' THEN TO_DATE(DEPARTURE_DTE, 'YYYY/MM/DD') END)
         (CASE WHEN p_order_col = 'TOTAL_PASSENGERS' THEN TOTAL_PASSENGERS END) DESC;

每个表达式按一个键排序。如果排序键不匹配,则表达式的结果为NULL - 所有行都获得相同的值,因此它不会影响排序。

答案 1 :(得分:1)

Gordon发布的示例答案中几乎没有性能影响。你并没有真正做三种不同的分类;它仍然是一个单一的排序操作。快速测试将显示:

select ename, sal, mgr 
from emp 
order by ( case when 'SAL' = 'SAL' then sal end ) 
,        ( case when 'SAL' = 'NAME' then ename end )

-----------------------------------------------------------------------------------
| Id  | Operation                  | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |      |       |       |    21 (100)|          |
|   1 |  SORT ORDER BY             |      |    14 |   196 |    21  (10)| 00:00:01 |
|   2 |   TABLE ACCESS STORAGE FULL| EMP  |    14 |   196 |    20   (5)| 00:00:01 |
-----------------------------------------------------------------------------------

但是,我会提醒您尝试编写单个"泛型"用于处理各种不同情况的SQL语句。虽然这种"聪明" SQL语句可以在功能上运行,它可能是性能的灾难。拥有单独的SQL语句会好得多。在此处发布的示例中,使用三个不同的SQL语句(每个语句都具有特定的ORDER BY子句,然后在输入参数上使用简单的IF THEN ELSE来确定要运行的SQL语句将相当容易