在 Oracle SQL 错误中将日期传递给存储过程:ORA-01858

Posted

技术标签:

【中文标题】在 Oracle SQL 错误中将日期传递给存储过程:ORA-01858【英文标题】:Passing date to Stored Procedure in Oracle SQL error : ORA-01858 【发布时间】:2020-09-17 08:41:33 【问题描述】:

我知道这个问题之前已经回答过了,我已经尝试了其中给出的解决方案,但没有成功。

我有一个如下的存储过程(我已经删除了非必要部分):

CREATE OR REPLACE PROCEDURE get_days(dt date)
IS
  given_date DATE := TO_DATE('dt', 'DD-MON-YYYY');
BEGIN
  get_day := RTRIM(TO_CHAR(dt1, 'DAY'));
  DBMS_OUTPUT.PUT_LINE ('The day of the given date is '||get_day||);
  DBMS_OUTPUT.PUT_LINE ('Execution  done successfully.');
END get_days;

我无法将日期传递给过程get_days。我尝试了以下方法:

BEGIN
  --Below I have listed all ways I have tried 
  get_days('12/12/12');
  get_days('12-12-12');
  get_days(date '2012-12-12');  
  get_days(TO_DATE( '01/01/2018', 'MM/DD/YYYY' ));
END;

我不知道出了什么问题,但我收到了这个错误 - ORA-01858: a non-numeric character was found where a numeric was expected。我在网上搜索过类似的错误,但没有任何帮助。我正在使用 Oracle 数据库 11g 快捷版。我的代码中可能有一些小错误,但我不明白它是什么。

非常感谢任何帮助!

【问题讨论】:

在 TO_DATE('dt', 'DD-MON-YYYY'); 'dt' 是一个字符串。但是为什么要将 DATE 传递给 TO_DATE? dt 可以以任何格式传递。我只是将其转换为DD-MM-YYYY 格式并存储到given_date 嗯,您将 dt 定义为 date,因此它没有格式。格式仅用于显示。顺便说一句,编写日期的最佳和最短的方法是标准 SQL 日期文字date '2012-12-12' 不,您已将形式参数声明为dt date - 所以它的数据类型是日期。它没有任何内在的人类可读格式。调用者必须传递一个实际的日期(如果它传递一个字符串并依赖于在某些时候会出错的隐式转换)。即使你有to_date(dt, ... 没有引号,而不是to_date('dt', ....,你也会做你自己的隐式转换,这也会中断。你不应该使用已经是日期的东西作为to_date()的第一个参数。 【参考方案1】:

错误的直接原因是你有

  given_date DATE := TO_DATE('dt', 'DD-MON-YYYY');

而不是

  given_date DATE := TO_DATE(dt, 'DD-MON-YYYY');

在您的版本中,您尝试将字符串文字 'dt' 转换为日期;该文字与您的 dt 参数无关。

但是,这仍然是错误的,因为 dt 已被声明为日期数据类型。如果你这样做,那么你真的在做:

  given_date DATE := TO_DATE(TO_CHAR(dt), 'DD-MON-YYYY');

它将使用您当前的会话NLS_DATE_FORMAT 将日期转换为字符串;如果这也不是 'DD-MON-YYYY'(或者足够接近 Oracle 将 fudge it 的东西),那么您仍然会收到错误,或者更糟糕的是,您可能不会注意到不正确的转换和无效结果。

所以去掉那个多余和危险的转换:

CREATE OR REPLACE PROCEDURE get_days(dt date)
IS
BEGIN
  DBMS_OUTPUT.PUT_LINE ('The day of the given date is ' || TO_CHAR(dt, 'FMDAY') || '.');
  DBMS_OUTPUT.PUT_LINE ('Execution  done successfully.');
END get_days;
/

您当然可以根据需要将日期名称分配给字符串变量。我已经使用the FM format modifier 来阻止它向右填充日期名称,所以你不需要修剪它 - 句号只是为了证明它有效。如果您想要混合大小写的日期名称,您可以使用'FMDay' 而不是'FMDAY'

你应该总是传入一个日期,而不是一个字符串:

set serveroutput on;

BEGIN
  get_days(date '2012-12-12');  
  get_days(TO_DATE( '01/01/2018', 'MM/DD/YYYY' ));
END;
/

The day of the given date is WEDNESDAY.
Execution  done successfully.
The day of the given date is MONDAY.
Execution  done successfully.


PL/SQL procedure successfully completed.

dt 可以以任何格式传递。我只是将其转换为 DD-MM-YYYY 格式并存储到 given_date

日期没有格式;它有一个你很少需要知道或关心的内部表示。当您查询日期值而不将其显式转换为字符串时,您的客户端会将其转换为人类可读的形式,通常使用NLS_DATE_FORMAT 来决定以什么格式显示它。所以dt 没有格式,并且即使调用者使用字符串进行调用(如前两个示例中所示),它们也会在调用之前隐式转换为日期 - 假设这是可能的;该过程永远不会看到这些字符串,它只会看到转换后的日期值。

【讨论】:

谢谢亚历克斯!你的代码对我有用。尽管日期以分配给它们的特定格式存储,这就是我将dt 转换为所需格式的原因。但事实并非如此,对吗? 您在回答中提到仅传递日期而不是字符串。但答案对我来说适用于 Dates 以及当你传递一个字符串时 - get_days('12/12/2012'); @Abhishek - 根据之前的评论查看我的更新。 get_days('12/12/2012') 确实在做get_days(to_date('12/12/2012')),所以它依赖于与当前会话的NLS_DATE_FORMAT 设置匹配的字符串。试试get_days('2012-12-12') 之类的东西 - 会抛出 ORA-01861 之类的东西,但请注意它是针对您的匿名块报告的,不是来自过程。 是的,这回答了我所有的疑惑。感谢您的详细回答! :)【参考方案2】:

我正在提供最佳答案来解决有关此程序的查询。也可以自行运行查看

DECLARE

dt1 DATE := TO_DATE(TO_CHAR('19/03/2021'), 'DD-MM-YYYY');

get_day VARCHAR2(15);

BEGIN
get_day := RTRIM(TO_CHAR(dt1, 'DAY'));

IF get_day IN ('SATURDAY', 'SUNDAY') THEN
dbms_output.new_line;
DBMS_OUTPUT.PUT_LINE 
('The day of the given date is '||get_day||' and it falls on weekend');

ELSE
dbms_output.new_line;
DBMS_OUTPUT.PUT_LINE ('The day of the given date is '||get_day||' and it does not fall 
on the weekend');

END IF;
DBMS_OUTPUT.PUT_LINE ('Execution  done successfully.');
END;

如果您对此有任何进一步的疑问,请询问我,我会尽快解决您的疑问。 如果有人想按用户输入日期,请​​问我....我也有这个代码。

【讨论】:

以上是关于在 Oracle SQL 错误中将日期传递给存储过程:ORA-01858的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Oracle SQL Developer 的存储过程中将表名列表作为参数传递?如何使用 PLSQL VARRAY 或嵌套表?

在 PL/SQL 存储过程中将多个变量传递给 WHERE 条件

将用户定义表中的日期值传递给 SQL Server 2008 存储过程

如何在 Python 中将日期传递给 MySQLdb?

在oracle 10g sql plus中将日期和时间插入时间戳

在 PL/SQL 存储过程中将表作为参数传递