在 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 存储过程