查询在存储过程之外运行,但在过程内部抛出错误

时间:2018-07-04 13:49:26

标签: oracle plsql

选择查询在程序包过程之外完美运行,但在程序过程内部完美运行 它会引发“ ORA-01858:在期望数字的位置找到了非数字字符”错误。

这是我的查询:

DECLARE
      v_general_number VARCHAR(11);
BEGIN
      SELECT 'Q' || TO_CHAR(TO_DATE(SYSDATE, 'MM/DD/YYYY'), 'Q') || '/'  ||   LPAD(TO_CHAR(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL), '3', '0')  || '/' || TO_CHAR(EXTRACT(YEAR FROM TO_DATE(SYSDATE, 'MM/DD/YYYY'))) INTO v_general_number FROM DUAL;
      DBMS_OUTPUT.PUT_LINE(v_general_number);
END;

效果很好。

但是在存储过程中,它将引发上面的错误。

这是我的程序:

   PROCEDURE save_request(p_name IN requests.suggested_name%TYPE,
                          p_urgency_type IN requests.urgency_type_id%TYPE) 
   AS

      v_general_number VARCHAR(11);

   BEGIN

   SELECT 'Q' || TO_CHAR(TO_DATE(SYSDATE, 'MM/DD/YYYY'), 'Q') || '/'  ||  LPAD(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL, 3, '0')  ||  '/' ||  TO_CHAR(EXTRACT(YEAR FROM TO_DATE(SYSDATE, 'MM/DD/YYYY')))  INTO v_general_number FROM DUAL;

   INSERT INTO requests (suggested_name,
                          urgency_type_id,
                          general_number 
      ) VALUES (
          p_name ,
          p_urgency_type ,
          v_general_number );



    END save_request;

我看不出是什么问题。任何建议,将不胜感激。

更新:

更改后,我解决了问题:

SELECT 'Q' || TO_CHAR(TO_DATE(SYSDATE, 'MM/DD/YYYY'), 'Q') || '/'  ||  LPAD(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL, 3, '0')  ||  '/' ||  TO_CHAR(EXTRACT(YEAR FROM TO_DATE(SYSDATE, 'MM/DD/YYYY')))   INTO v_general_number FROM DUAL;

对此:

SELECT 'Q' || TO_CHAR(SYSDATE, 'Q') || '/'  ||  LPAD(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL, 3, '0')  ||  '/' ||  TO_CHAR(EXTRACT(YEAR FROM SYSDATE))  INTO v_general_number FROM DUAL;

它奏效了。

但是我仍然不明白为什么相同的查询在过程外仍然起作用,但是在过程内抛出错误。

1 个答案:

答案 0 :(得分:6)

SYSDATE已经是日期,所以

TO_DATE(SYSDATE, 'MM/DD/YYYY')

完全没有道理。您正在将隐式转换为字符串,然后将其显式转换为日期,即有效地:

TO_DATE(TO_CHAR(SYSDATE, <NLS_DATE_FORMAT>), 'MM/DD/YYYY')

是否起作用取决于会话的NLS_DATE_FORMAT设置:

alter session set nls_date_format = 'MM/DD/YYYY';
select TO_DATE(SYSDATE, 'MM/DD/YYYY') from dual;

TO_DATE(SY
----------
07/04/2018

select TO_CHAR(TO_DATE(SYSDATE, 'MM/DD/YYYY'), 'YYYY-MM-DD') from dual;

TO_CHAR(TO
----------
2018-07-04

alter session set nls_date_format = 'DD/MM/YYYY';
select TO_DATE(SYSDATE, 'MM/DD/YYYY') from dual;

TO_DATE(SY
----------
07/04/2018

select TO_CHAR(TO_DATE(SYSDATE, 'MM/DD/YYYY'), 'YYYY-MM-DD') from dual;

TO_CHAR(TO
----------
2018-04-07

alter session set nls_date_format = 'DD-MON-YYYY';
select TO_DATE(SYSDATE, 'MM/DD/YYYY') from dual;

ORA-01858: a non-numeric character was found where a numeric was expected

alter session set nls_date_format = 'YYYY-MM-DD';
select TO_DATE(SYSDATE, 'MM/DD/YYYY') from dual;

ORA-01843: not a valid month

请注意,前两个都“起作用”,但实际上给您不同的日期。 (该月晚些时候,第12个之后,第二个也将因无效的月份错误而失败。)

您从中运行匿名块的会话以及从中执行过程的会话仅具有不同的NLS设置。这就是为什么您不应该依赖隐式转换或NLS设置的原因...

无论如何,删除不必要的转换:

SELECT 'Q' || TO_CHAR(SYSDATE, 'Q')
  || '/' || LPAD(TO_CHAR(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL), '3', '0') 
  || '/' || TO_CHAR(EXTRACT(YEAR FROM SYSDATE))
INTO v_general_number
FROM DUAL;

或对分配执行相同的操作,而不是从对偶中选择:

v_general_number := 'Q' || TO_CHAR(SYSDATE, 'Q')
  || '/' || LPAD(TO_CHAR(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL), '3', '0')
  || '/' || TO_CHAR(EXTRACT(YEAR FROM SYSDATE));

,甚至,作为替代:

v_general_number := TO_CHAR(SYSDATE, '"Q"Q"/"')
  || TO_CHAR(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL, 'FM000')
  || TO_CHAR(SYSDATE, '"/"YYYY');

在日期格式模型中使用字符文字来替换斜杠的串联,并在序列号中使用格式模型,因此您无需再用零填充。 (并且您实际上并不需要变量,可以在插入的values子句中进行相同的构造。)