在 oracle 11g 中将数据类型排序为 varchar2 的日期字段

sorting date field with datatype as varchar2 in oracle 11g

我需要对日期值为数字格式的 varchar2 数据类型列进行排序,格式为 YYMMMMYY

取值范围为

3
6
9
12
103
.
.
9909
9912

长度为4个字符的值表示1900到1999之间的年份 少于4个字符表示年份以2000年及以上开始。

我尝试使用 LPAD(fieldname, 4,'0'),其中 returns 值为

0003 this is nothing but 2000/03
0006 ------------------- 2000/06
0009
0012
0103 ------------------- 2001/03
.
.
9912 ------------------- 1999/12

如何根据从 1900 年开始的年份值按升序对列进行排序。 谁能给我解决方案....

您需要先区分现有的四位数字值,以便在填充其余部分之前知道它们是 20 世纪的日期。

然后将其转换为日期和 fiddle 格式以获得您需要的排序顺序:

 select to_char( expanded_dt, 'MMYYYY') as switched_dt
 from (
     select to_date(
                  case when length(dt) = 4 then '19'||dt
                  else '20'||lpad(dt,4,'0') end 
             , 'YYYYMM' )
                 as expanded_dt
      from your_table
     )
order by 1 asc
/

首先,将 DATE 值存储为 VARCHAR2 并仅使用 部分元素。在漫长的运行中,你应该首先修复你的设计。

作为解决方法,如果年份值在 1950-2049 范围内,您可以使用 RR 格式作为 两位数字年.

TO_DATE(LPAD(date_column, 4,'0'), 'RRMM')

LPAD 将添加所需的前导零。

例如,

SQL> alter session set nls_date_format='MM/DD/YYYY';

Session altered.

SQL> WITH dates AS(
  2  SELECT '3' dt FROM dual UNION ALL
  3  SELECT '6' dt FROM dual UNION ALL
  4  SELECT '9' dt FROM dual UNION ALL
  5  SELECT '12' dt FROM dual UNION ALL
  6  SELECT '103' dt FROM dual UNION ALL
  7  SELECT '9909' dt FROM dual UNION ALL
  8  SELECT '9912' dt FROM dual
  9  )
 10  SELECT dt,
 11         TO_DATE(LPAD(dt, 4,'0'), 'RRMM') date_val
 12  FROM dates
 13  ORDER BY date_val;

DT   DATE_VAL
---- ----------
9909 09/01/1999
9912 12/01/1999
3    03/01/2000
6    06/01/2000
9    09/01/2000
12   12/01/2000
103  03/01/2001

7 rows selected.

SQL>

因此,上面的查询为您提供了所需的输出,日期值现在按升序排序。

您可以根据值的长度添加一个世纪标记:

select value,
  case when length(value) = 4 then '19' else '20' end || lpad(value, 4, '0') as dt
from t
order by case when length(value) = 4 then '19' else '20' end || lpad(value, 4, '0');

     VALUE DT   
---------- ------
      9909 199909 
      9912 199912 
         6 200006 
         9 200009 
        12 200012 
       103 200103 

或者使用相同的东西并转换为日期,默认为每个月的第一天:

select value, to_date(case when length(value) = 4 then '19' else '20' end
    || lpad(value, 4, '0'), 'YYYYMM') as dt
from t
order by to_date(case when length(value) = 4 then '19' else '20' end
    || lpad(value, 4, '0'), 'YYYYMM');

     VALUE DT       
---------- ----------
      9909 1999-09-01 
      9912 1999-12-01 
         6 2000-06-01 
         9 2000-09-01 
        12 2000-12-01 
       103 2001-03-01 

如果您只查看 1950-2049 年 Y2K 安全范围内的日期,您可以跳过世纪部分并改用 RR 日期模型,不过因为这可能会在以后给您带来问题,所以没有'与使用长度前缀世纪相比,确实没有任何优势:

select value, to_date(lpad(value, 4, '0'), 'RRMM') as dt
from t
order by to_date(lpad(value, 4, '0'), 'RRMM');

     VALUE DT       
---------- ----------
      9909 1999-09-01 
      9912 1999-12-01 
         6 2000-06-01 
         9 2000-09-01 
        12 2000-12-01 
       103 2001-03-01 

SQL Fiddle.