查询在存储过程之外工作但在过程内部会引发错误

Nig*_*ade 1 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;
Run Code Online (Sandbox Code Playgroud)

它工作得很好.

但是在存储过程中它会抛出上面的错误.

这是我的程序:

   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;
Run Code Online (Sandbox Code Playgroud)

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

更新:

我在改变之后解决了这个问题:

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;
Run Code Online (Sandbox Code Playgroud)

对此:

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;
Run Code Online (Sandbox Code Playgroud)

它奏效了.

但我仍然不明白为什么相同的查询在过程之外工作,但在过程中抛出错误.

Ale*_*ole 6

SYSDATE 已经是个约会,所以

TO_DATE(SYSDATE, 'MM/DD/YYYY')
Run Code Online (Sandbox Code Playgroud)

没有意义.您正在对字符串进行隐式转换,然后显式转换为日期,即有效:

TO_DATE(TO_CHAR(SYSDATE, <NLS_DATE_FORMAT>), 'MM/DD/YYYY')
Run Code Online (Sandbox Code Playgroud)

它是否有效取决于您的会话的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
Run Code Online (Sandbox Code Playgroud)

请注意,前两个"工作",但实际上给你不同的日期.(在本月晚些时候,在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;
Run Code Online (Sandbox Code Playgroud)

或者使用赋值执行相同的操作,而不是从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));
Run Code Online (Sandbox Code Playgroud)

甚至,作为替代方案:

v_general_number := TO_CHAR(SYSDATE, '"Q"Q"/"')
  || TO_CHAR(SEQ_EDP_GENERAL_NUM_ID.NEXTVAL, 'FM000')
  || TO_CHAR(SYSDATE, '"/"YYYY');
Run Code Online (Sandbox Code Playgroud)

它使用日期格式模型中的字符文字来重新替换斜杠的串联,以及序列号的格式模型,因此您不需要用零填充它.(而且你真的不需要变量,你可以在insert的values子句中做同样的构造.)