【问题标题】:"ORA-01861: literal does not match format string" Error in PL/SQL“ORA-01861:文字与格式字符串不匹配”PL/SQL 中的错误
【发布时间】:2021-10-26 23:10:54
【问题描述】:

谁能帮我理解下面的代码行做错了什么?

res_start_time_ := to_date(to_char(account_date_, 'YYYYMMDD ') || sched_ftime_, 'YYYYMMDD HH24:MI');

res_start_time_account_date_ 属于 DATE 类型。

sched_ftime_是VARCHAR2类型,可以为NULL。

在测试场景中,当account_date_ 有值且sched_ftime_ 有NULL 值时,我收到ORA-01861: literal does not match format string 错误。

谁能向我解释我做错了什么以及如何摆脱这个错误?

【问题讨论】:

  • @GordonLinoff 在 Oracle 中,DATE 数据类型总是具有年、月、日、小时、分钟和秒部分。将TO_DATE 与时间组件一起使用是完全有效的。
  • 我无法复制错误db<>fiddle。请edit您的问题包含一个带有完整(最小)PL/SQL 块的minimal reproducible example,我们可以执行该块来演示错误,以便我们可以复制您的问题。
  • 你有一些样本值吗?通常不需要在 DATE 和 VARCHAR2 之间来回转换。
  • @MT0 尝试将trunc(sysdate)(或其他任何内容,如日期)添加到sched_ftime_ 而不是NULL.. 和瞧。
  • @Georgy 问题指出“当account_date_ 存在值且sched_ftime_ 存在NULL 时,我得到ORA-01861:文字与格式字符串不匹配错误。”因此,如果您对sched_ftime_ 使用非NULL 值,则输入与说明它是NULL 值的问题不匹配。

标签: oracle plsql oracle11g to-date


【解决方案1】:

源代码试图形成一个字符串,该字符串可以转换为具有日期值和时间值的日期。

to_char(account_date_, 'YYYYMMDD ') 将日期值转换为以空格结尾的 9 个字符的字符串,从而允许使用字符串连接应该包含小时和分钟的值。连接后,它会尝试将其转换为精确到一分钟的日期值。

但是,如果sched_ftime_ 的非空值不是可以转换为 HH24:MI 的形式,例如'123456' 太长,只能解释为小时和分钟。

这可以复制:

ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
DECLARE
  res_start_time_ DATE;
  account_date_   DATE := TRUNC(SYSDATE);
  sched_ftime_    VARCHAR2(20) := '123456'; /* this value fails */
BEGIN
  res_start_time_ := to_date(
                       to_char(account_date_, 'YYYYMMDD ') || sched_ftime_,
                       'YYYYMMDD HH24:MI'
                     );
  DBMS_OUTPUT.PUT_LINE(res_start_time_);
END;
/

ORA-01861: literal does not match format string
ORA-06512: at line 6

但是一个较短的值,例如'1234' 可以解释为小时和分钟:

DECLARE
  res_start_time_ DATE;
  account_date_   DATE := TRUNC(SYSDATE);
  sched_ftime_    VARCHAR2(20) := '1234'; /* this value works */
BEGIN
  res_start_time_ := to_date(
                       to_char(account_date_, 'YYYYMMDD ') || sched_ftime_,
                       'YYYYMMDD HH24:MI'
                     );
  DBMS_OUTPUT.PUT_LINE(res_start_time_);
END;
/

1 rows affected

dbms_output:
2021-08-27 12:34:00

因此,我建议您通过限制第二个参数的长度来保护转换为日期,例如最多 4 个字符,可能使用 substr()

DECLARE
  res_start_time_ DATE;
  account_date_   DATE := TRUNC(SYSDATE);
  sched_ftime_    VARCHAR2(20) := '123456'; /* this value gets truncated */
BEGIN
  res_start_time_ := to_date(
                       to_char(account_date_, 'YYYYMMDD ') 
                               || substr(sched_ftime_,1,4),
                       'YYYYMMDD HH24:MI'
                     );
  DBMS_OUTPUT.PUT_LINE(res_start_time_);
END;
/

您可能需要对该 varchar2 值进行其他验证,以便它们也都是数字。或者,如果您在小时和分钟值中包含冒号,则总长度需要为 5 个字符。简而言之,您需要检查小时和分钟,以便它们合乎逻辑且有效。

nb:感谢 MT0 的源数据库我扩展的小提琴 here

【讨论】:

  • 这给出了预期的错误,但它与 OP 问题中的输入条件不匹配,因为它们声明“当 account_date_ 有值且 @ 为 NULL 时,我得到了 ORA-01861: literal does not match format string 错误987654332@。”这是sched_ftime_ 不是NULL 时的答案。
  • 问题状态为 it can be null ,而不是它为空。正如您所演示的,错误不是由该参数中的 null 触发的,并且如上所示,当不为 null 并且无法转换为 HH24:MI 时会发生错误:当不为 null 时,OP 需要验证该格式兼容性的第二个参数.
【解决方案2】:

问题的根源是您允许时间组件 (sched_ftime_) 假定您事先不知道确切格式的值。您可以这样做,但它带有编码要求。您需要建立允许,然后在运行时派生 FORMAT SPECIFICATION,如果值与允许的模式不匹配,则抛出错误。什么是允许的,验证可以根据您的需要简单或复杂。作为演示,以下函数查找 5 种模式:hh24mi、hh24miss、hh24:mi、hh24:mi:ss 和 null。对于那些它返回适当的日期,对于其他任何东西它都返回一个应用程序定义的异常。

create or replace 
function get_actual_date_time( account_date_  date
                             , sched_ftime_   varchar2
                             ) 
  return date
is 
  k_format_base          constant varchar2(8)  := 'yyyymmdd';
  k_bad_time_format_msg  constant varchar2(32) := ' invalid time specification.';  
  -- declare regexp for valid time formats 
  k_regx_time_hh24mm     constant varchar2(7)  := '^\d{4}$';
  k_regx_time_hh24mmss   constant varchar2(7)  := '^\d{6}$'; 
  k_regx_time_hh24mm_s   constant varchar2(11) := '^\d\d:\d\d$'; 
  k_regx_time_hh24mmss_s constant varchar2(16) := '^\d\d:\d\d:\d\d$';  
  
  -- declare actual format specification corresponding to valid format
  k_fmt_time_hh24mm      constant varchar2(6)  := 'hh24mi';
  k_fmt_time_hh24mmss    constant varchar2(8)  := 'hh24miss'; 
  k_fmt_time_hh24mm_s    constant varchar2(7)  := 'hh24:mi';
  k_fmt_time_hh24mmss_s  constant varchar2(10) := 'hh24:mi:ss';
  
  l_time_format varchar2(16); 
begin     
  case when sched_ftime_ is null then
            l_time_format := null;
       when regexp_like ( sched_ftime_,k_regx_time_hh24mm) then
            l_time_format := k_fmt_time_hh24mm; 
       when regexp_like ( sched_ftime_,k_regx_time_hh24mmss) then
            l_time_format := k_fmt_time_hh24mmss;  
       when regexp_like ( sched_ftime_,k_regx_time_hh24mm_s) then
            l_time_format := k_fmt_time_hh24mm_s;  
       when regexp_like ( sched_ftime_,k_regx_time_hh24mmss_s) then
            l_time_format := k_fmt_time_hh24mmss_s;  
       else 
           raise_application_error( -20109,'''' || sched_ftime_ || '''' || k_bad_time_format_msg); 
  end case; 
       
  return to_date(to_char(account_date_,k_format_base) || sched_ftime_ 
                , k_format_base || l_time_format);
end get_actual_date_time;

请记住,以上内容只是一个示例,还有许多需要改进的地方。例如,将接受 99:99:99 的时间规范,即使它显然是无效的时间规范。但它符合'^\d\d:\d\d:\d\d$' 的验证。它也不会尝试验证有效时间规范06:45 PM。见fiddle here

【讨论】:

    猜你喜欢
    • 2014-04-27
    • 2016-04-02
    • 1970-01-01
    • 2015-02-13
    • 2017-08-24
    • 2015-01-11
    • 2010-11-26
    • 2012-10-09
    相关资源
    最近更新 更多