【问题标题】:How to convert partial dates in Oracle SQL如何在 Oracle SQL 中转换部分日期
【发布时间】:2017-01-13 00:34:29
【问题描述】:

我需要比较 2 个差异表中的 2 个日期列,它们都是 varchar2 类型。两列都有基于数据的部分日期和完整日期。

T1:

ID    Partial_date1  
1     19-DEC-2016  
2     06-MAY-2015  
3     2016  
4  
5     AUG-2016  
6     16-NOV-2015 00:00  
7     01-JAN-2016 

T2:

ID     Partial_date2  
1     09-JAN-2016  
2     2016  
3     SEP-2015  
4   
5     23-MAR-2016 00:00  
6     15-MAY-2015  
7  

我想搜索具有完整日期的所有记录(因为无法转换部分日期),仅选择具有完整日期的记录,我使用了长度>10。这是我编写的 SQL,但似乎不起作用。

select t1.id from t1, t2
where t1.id =t2.id
and length(t1.partial_date1)>10
and length(t2.partial_date2)>10
and to_date(t1.partial_date1,'DD-MON-YYYY') > to_date(t2.partial_date2,'DD-MON-YYYY')

我得到一个错误 - ORA-01830:日期格式图片在转换整个输入字符串之前结束 或文字与格式字符串不匹配。

我做错了什么?如何获得正确的结果?

【问题讨论】:

  • 您的意思是在传递给TO_DATE 之前先取SUBSTR(t1.partial_date1, 10),RHS 也一样吗?
  • 你没有日期,你有 varchar2 代表日期。这从来都不是一件好事。请出示样本数据。
  • 我不需要取子字符串,我只想选择完整日期的记录进行比较,因为我们无法转换部分日期。
  • 您不能将字符串'23-MAR-2015 10:00:00' 转换为to_date() 格式模型'DD-MON-YYYY' 的日期,这不是to_date() 的工作方式。如果要将完整字符串转换为FULL日期,则需要使用全格式模型。
  • True 或 False:列中的所有字符串正好是两种格式之一,“DD-MON-YYYY”或“DD-MON-YYYY HH24:MI:SS”。 (或其他一些完整格式,但对于所有具有完整日期的字符串都相同。)如果这是 FALSE,那么我认为您将无法编写任何有意义的内容。如果为 TRUE,只需在 WHERE 子句中使用适当的 FULL 格式模型即可。

标签: sql oracle


【解决方案1】:

当字符串以 DD-MMM-YYYY 开头时,您似乎认为日期已完成。您可以使用REGEXP_LIKE 来查找此类行:

where regexp_like(partial_date, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')

(您可能需要根据需要调整模式,例如将[[:upper:]] 替换为[[:alpha:]]。)

为了转换包含文本月份的日期,您应该使用带有语言参数的TO_DATE

to_date(partial_date, 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')

一个可能的查询:

select tt1.id 
from 
(
  select
    id,
    to_date(substr(partial_date1, 1, 11), 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
      as dt
  from t1
  where regexp_like(partial_date1, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
) tt1
join 
(
  select
    id,
    to_date(substr(partial_date2, 1, 11), 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
      as dt
  from t2
  where regexp_like(partial_date2, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
) tt2 on tt2.id = tt1.id and tt2.dt < tt1.dt;

但是,请记住,您仍在处理字符串。像这样的“日期”转换会失败并使您的查询崩溃:

  • 2017 年 2 月 30 日
  • 01-YAN-2017
  • 99-XXX-9999

因此,根据数据质量,您最好编写一个 PL/SQL 函数来捕获转换错误。

【讨论】:

    【解决方案2】:

    当您在 where 子句中编写条件时,oracle 可能会将其用作访问谓词。它的功能to_date(t1.partial_date1,'DD-MON-YYYY') 用于任何行。

    这就是您收到错误的原因。

    我看到了两种方式:

    第一种方法是使用子查询得到一个缩小的数据集,substr 只有 10 个符号然后转换它

    with t1(id,partial_date1) as 
    (
        select 1,'19-DEC-2016' from dual union all
        select 2,'06-MAY-2015' from dual union all
        select 3, '2016' from dual union all
        select 4,'' from dual union all
        select 5,'AUG-2016' from dual union all
        select 6,'16-NOV-2015 00:00' from dual union all
        select 7, '01-JAN-2016' from dual
    ), t2(id,partial_date2) as 
    (
        select 1,'09-JAN-2016' from dual union all
        select 2,'2016' from dual union all
        select 3,'SEP-2015' from dual union all
        select 4,'' from dual union all
        select 5,'23-MAR-2016 00:00' from dual union all
        select 6,'15-MAY-2015' from dual union all
        select 7,'' from dual
    )
    select * 
    from 
        (select 
             t1.id,
             partial_date1,
             partial_date2
         from 
             t1, t2
         where 
             t1.id =t2.id
             and length(t1.partial_date1) > 10
             and length(t2.partial_date2) > 10
             and rownum > 0)
    where 
        to_date(substr(partial_date1,1,10),'DD-MON-YYYY') > to_date(substr(partial_date2,1,10),'DD-MON-YYYY');
    /
    

    第二种方法是将任何格式显式转换为正确格式

    with t1(id,partial_date1) as (
    select 1,'19-DEC-2016' from dual union all
    select 2,'06-MAY-2015' from dual union all
    select 3, '2016' from dual union all
    select 4,'' from dual union all
    select 5,'AUG-2016' from dual union all
    select 6,'16-NOV-2015 00:00' from dual union all
    select 7, '01-JAN-2016' from dual)
    ,t2(id,partial_date2) as (
    select 1,'09-JAN-2016' from dual union all
    select 2,'2016' from dual union all
    select 3,'SEP-2015' from dual union all
    select 4,'' from dual union all
    select 5,'23-MAR-2016 00:00' from dual union all
    select 6,'15-MAY-2015' from dual union all
    select 7,'' from dual)
    select * from( 
    select t1.id,
          case
              when regexp_like(t1.partial_date1,'\d{1,2}-\w{3}-\d{4} \d{1,2}:\d{2}') then to_date(t1.partial_date1,'dd-MON-yyyy HH24:MI')
              when regexp_like(t1.partial_date1,'\d{1,2}-\w{3}-\d{4}') then to_date(t1.partial_date1,'dd-MON-yyyy')
              when regexp_like(t1.partial_date1,'\w{3}-\d{4}') then to_date(t1.partial_date1,'MON-yyyy')
              when regexp_like(t1.partial_date1,'\d{4}') then to_date(t1.partial_date1,'yyyy')
          end as pd1,
          case
              when regexp_like(t2.partial_date2,'\d{1,2}-\w{3}-\d{4} \d{1,2}:\d{2}') then to_date(t2.partial_date2,'dd-MON-yyyy HH24:MI')
              when regexp_like(t2.partial_date2,'\d{1,2}-\w{3}-\d{4}') then to_date(t2.partial_date2,'dd-MON-yyyy')
              when regexp_like(t2.partial_date2,'\w{3}-\d{4}') then to_date(t2.partial_date2,'MON-yyyy')
              when regexp_like(t2.partial_date2,'\d{4}') then to_date(t2.partial_date2,'yyyy')
          end as pd2
    from t1,t2
    where t1.id = t2.id)
    where pd1 > pd2
    /
    

    【讨论】:

      【解决方案3】:
      case
                  when PurchaseDate = '0' then NULL
                  when right(PurchaseDate, 4) = '0000' then convert(date, left(PurchaseDate,4) + '1231', 112)
                  when RIGHT(PurchaseDate, 2) = '00' then DATEADD(day,-1,DATEADD(month,cast(left(RIGHT(Purchasedate,4),2) AS INT),DATEADD(year,cast(LEFT(Purchasedate, 4) AS int)-1900,0)))
                  else convert(date, ltrim(rtrim(cast(PurchaseDate as varchar(50)))), 112)
                  end
      

      道歉。这是我手机上的。这做了几件事。它采用不同的格式,加上它对丢失的日期和月份的计数器。

      希望这会有所帮助。

      【讨论】:

      • 没用。该帖子明确标记为 Oracle。 Oracle 中没有 LEFT、RIGHT 和 DATEADD 函数。标签在那里是有原因的。出于这个原因-1。
      • 我确实说过我是用手机工作的。 :D 将 right 替换为 oracle 版本:SUBSTR(PurchaseDate, -1, 2)。对于 dateadd,只需使用 plus(+) 和 minus(-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-13
      • 1970-01-01
      • 1970-01-01
      • 2018-10-29
      • 2016-07-27
      相关资源
      最近更新 更多