获取 ORA-01861 - 文字与仅 SQLPlus 上的格式字符串不匹配

Getting ORA-01861 - Literal does not match format string on SQLPlus Only

我有一个 PL/SQL 过程需要以下 arguments/parameters

Procedure create_test(mode_in in number, value1_in in number, 
        value2_in in number, value3_in in varchar2);

我正在使用以下 pl/sql 块来调用和执行过程

DECLARE
  lv_mode NUMBER;
  lv_value1_in NUMBER;
  lv_value2_in NUMBER;
  lv_value3 VARCHAR2(3);
BEGIN
  lv_mode := 1;
  lv_value1_in := 1;
  lv_value2_in := 1;
  lv_value3_in := 'ES';

  CREATE_TEST(
    mode_in => lv_mode ,
    value1_in => lv_value1_in,
    value2_in => lv_value2_in,
    value3_in => lv_value3_in
  );
--rollback; 
END;
/

如果我将上面的 sql 块粘贴到 SQLDeveloper 中并执行它,它 运行 没有问题。 如果我把它放在一个文件中并通过 SQL plus 执行它,我会得到以下错误(如果 运行 直接在 SQLPLus 中也会出现同样的问题):

ORA-01861: literal does not match format string

通常当我收到此错误时,问题通常与日期有关。我不确定上面有什么问题,因为没有涉及日期 - 特别是考虑到相同的 SQL 块在 IDE 而不是 SQLPLus 中工作。 SQLPlus 处理文字的方式与 IDE 略有不同吗?

我的猜测是 SQLPlus 中的某些参数处理方式不同 - 但哪个参数?

您对语句“TO_DATE(SYSDATE, 'DD-MON-YYYY HH:MI:SS')”的使用存在根本性缺陷。 to_date 函数将字符串作为其第一个参数,但您传递的是 SYSDATE,这是一个日期。这会强制 oracle 首先使用 NLS_DATE_FORMAT 的会话级别设置将 SYSDATE 隐式转换为字符串。获得该字符串后,它会将其转换回 DATE,并且必须假定派生的字符串与您提供的 'DD-MON-YYYY HH:MI:SS' 格式匹配。它在 SQL Dev 中工作,因为您将其配置为将 NLS_DATE_FORAMT 设置为 'DD-MON-YYYY HH:MI:SS',但这不是数据库默认格式,因此它在 sqlplus 下失败。您需要解决将日期 (sysdate) 传递给 to_date 函数的基本缺陷,而不是随意使用 NLS 设置。

有趣的错误:TO_DATE(SYSDATE, 'DD-MON-YYYY HH:MI:SS')

只需在过程中将此表达式替换为 SYSDATE..


SYSDATE函数返回的数据类型是DATE:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions172.htm


TO_DATE 函数需要 CHARVARCHAR2NCHARNVARCHAR2 类型作为其第一个参数:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions183.htm


该过程将 SYSDATE(DATE 类型)作为第一个参数传递,该参数应为字符串(VARCHAR、CHAR 等)。在这种情况下,Oracle 在内部使用 TO_CHAR 函数执行 DATE 到 CHHAR/VARCHAR2 的隐式转换。
可以说上面的表达式在内部被转换为:

TO_DATE(  TO_CHAR( sysdate ) , 'DD-MON-YYYY HH:MI:SS' )

可以在此处找到有关隐式转换规则的更多信息(滚动到部分:“数据转换 - 隐式和显式数据转换”):
https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements002.htm


上面的一个重要片段link:

Implicit conversion depends on the context in which it occurs and may not work the same way in every case. For example, implicit conversion from a datetime value to a VARCHAR2 value may return an unexpected year depending on the value of the NLS_DATE_FORMAT parameter.


换句话说 - TO_CHAR( some-date ) 没有第二个参数(格式)使用从会话中获取的 NLS_DATE_FOMRAT 变量值。

如果在SQL-Developer上勾选这个参数,大概是:'DD-MON-YYYY HH:MI:SS'(在SQL-Developer中这个参数的值是在option中配置的:Tools/Preferences/Database/NLS

但是如果你检查 SQL-Plus 中的 NLS_DATE_FORMAT 值,它将与 'DD-MON-YYYY HH:MI:SS' 不同。 SQL-Plus 根据环境的语言设置(Windows、Linux 等)和 NLS_LANG 环境变量(如果存在)确定 NLS 设置的值。

您可以使用以下方法在您的会话中更改此参数:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH:MI:SS';
并且(旧)程序将起作用。