ORA-00984:此处不允许使用列 - 动态SQL

时间:2015-02-12 08:21:37

标签: sql oracle plsql sqlplus

这是我的包裹代码。

CREATE OR REPLACE PACKAGE BODY FMSSMART.GENERIC_PURGER
AS

   PROCEDURE GET_PARTITIONID_JULIAN (i_date                   IN     DATE,
                              i_number_of_partitions   IN     NUMBER,
                              o_partitionID               OUT NUMBER)
   IS
   BEGIN
      SELECT MOD (TO_NUMBER (TO_CHAR (TO_DATE (i_date, 'YYYYMMDD'), 'j')),
                  i_number_of_partitions)
        INTO o_partitionID
        FROM DUAL;

      DBMS_OUTPUT.put_line (o_partitionID);
   END GET_PARTITIONID_JULIAN;

 PROCEDURE DELETE_TBL_SML
    (
      i_tablename IN VARCHAR2,
      o_retcode OUT NUMBER,
      o_errormsg OUT VARCHAR2
    )
     IS

    stmt VARCHAR2(1000);
    o_start_time DATE;
    o_end_time DATE;

    BEGIN
      select to_char(sysdate, 'YYYYMMDDHH24MISS') into o_start_time from dual;
      stmt := 'DELETE FROM ' ||i_tablename;
      EXECUTE IMMEDIATE stmt;
      COMMIT;
      select to_char(sysdate, 'YYYYMMDDHH24MISS') into o_end_time from dual;

    EXCEPTION WHEN OTHERS THEN
    o_retcode := SQLCODE;
    o_errormsg := substr(SQLERRM, 1, 200);


    INSERT INTO AUDIT_PROC_TBL (error_number, error_message, package_name, procedure_name, start_time, end_time) VALUES (o_retcode, o_errormsg, 'GENERIC_PURGER','DELETE_TBL_SML', o_start_time, o_end_time);
    return;
    END DELETE_TBL_SML;

 PROCEDURE INSERT_AUDIT_PROC_TBL
 (
      i_retcode IN NUMBER,
      i_errormsg IN VARCHAR2,
      i_package_name IN  VARCHAR2,
      i_procedure_name IN VARCHAR2,
      i_start_time IN DATE,
      i_end_time IN DATE
 )
   IS
    BEGIN
          EXECUTE IMMEDIATE  'INSERT INTO AUDIT_PROC_TBL (error_number, error_message, package_name, procedure_name, start_time, end_time) VALUES (i_retcode, i_errormsg, i_package_name,  i_procedure_name,i_start_time,  i_end_time)';
          COMMIT;
          RETURN;
   END INSERT_AUDIT_PROC_TBL;

END GENERIC_PURGER;
/

执行时:

set autocommit off;
set serveroutput on size 1000000;
ALTER SESSION SET NLS_DATE_FORMAT='YYYYMMDD HH24:MI:SS';
EXECUTE INSERT_AUDIT_PROC_TBL(-1, 'NA', 'GENERIC_PURGER','DELETE_TBL_SML', '20150212164527', '20150212164527');

我遇到了一个错误,它给了我:

Session altered.

BEGIN INSERT_AUDIT_PROC_TBL(-1, 'NA', 'GENERIC_PURGER','DELETE_TBL_SML', '20150212164527', '20150212164527'); END;

*
ERROR at line 1:
ORA-00984: column not allowed here
ORA-06512: at "FMSSMART.INSERT_AUDIT_PROC_TBL", line 12
ORA-06512: at line 1

1 个答案:

答案 0 :(得分:3)

您获得的错误不是来自您如何调用该过程,而是该过程正在执行的操作。针对FMSSMART.INSERT_AUDIT_PROC_TBL过程的第12行报告ORA-00984错误,该错误为:

EXECUTE IMMEDIATE  'INSERT INTO AUDIT_PROC_TBL (error_number, error_message, package_name, procedure_name, start_time, end_time) VALUES (i_retcode, i_errormsg, i_package_name,  i_procedure_name,i_start_time,  i_end_time)';

当您不需要时,您可以在这里使用动态SQL;没有任何动态和静态的SQL可以解决问题:

INSERT INTO AUDIT_PROC_TBL (error_number, error_message, package_name,
  procedure_name, start_time, end_time)
VALUES (i_retcode, i_errormsg, i_package_name,  
  i_procedure_name,i_start_time,  i_end_time);

为了将来参考,当您使用动态SQL时,您需要对传入的值使用绑定变量;你在动态SQL语句中有一个绑定占位符,它由冒号表示,例如:var1,然后使用using子句提供实际值。在原始版本中,i_retcode被解释为列名,而不是您的变量,这超出了动态上下文的范围。所以你会使用类似的东西:

EXECUTE IMMEDIATE  'INSERT INTO AUDIT_PROC_TBL (error_number, '
  || 'error_message, package_name, procedure_name, start_time, end_time) '
  || 'VALUES (:retcode, :errormsg, :package_name,  :procedure_name, '
  || ':start_time,  :end_time)'
USING i_retcode, i_errormsg, i_package_name,  i_procedure_name,
  i_start_time,  i_end_time;

为了便于阅读,我将声明拆分为多行;通过||连接意味着最终的字符串与它在一行中的全部相同。


我还有一些超出问题范围的其他观察结果:

  • 您在会话中设置NLS_DATE_FORMAT,然后依赖隐式转换;无论如何奇怪地使用与你的字符串不匹配的格式。最好在您的通话中明确传递日期值,例如to_date('20150212164527', 'YYYYMMDDHH24MISS')timestamp字面值。
  • DELETE_TBL_SML个套餐内部的情况更糟,因为您仍然依赖于会话NLS设置,您无法始终控制,并且您明确转换日期到字符串只是隐式转换它们。而不是select to_char(sysdate, 'YYYYMMDDHH24MISS') into o_start_time from dual;只是o_start_time := sysdate)
  • 在程序中提交或回滚通常被视为不良做法;调用者决定是否以及何时提交更好,因为它可能正在做其他事情,您的程序不知道应该将其视为同一事务的一部分。
  • 捕获您无法处理的异常,尤其是when others,通常是一个错误。虽然您在此处将代码和消息返回给调用者,但他们必须查找它。让异常传播回调用者几乎总是更好 - 这也可以提交给提交/回滚决定。

删除程序可以简化为:

PROCEDURE DELETE_TBL_SML(i_tablename IN VARCHAR2) IS
  o_start_time DATE;
  o_end_time DATE;
BEGIN
  o_start_time := sysdate;
  EXECUTE IMMEDIATE 'DELETE FROM ' ||i_tablename;
  o_end_time := sysdate;

  INSERT INTO AUDIT_PROC_TBL (error_number, error_message, package_name,
    procedure_name, start_time, end_time)
  VALUES (SQLERRCODE, substr(SQLERRM, 1, 200), 'GENERIC_PURGER',
    'DELETE_TBL_SML', o_start_time, o_end_time);
END DELETE_TBL_SML;

除非你只想审计错误,在这种情况下你需要将execute包含在它自己的子块中;然后将其称为exec FMSSMART.GENERIC_PURGER.DELETE_TBL_SML(<your table name>)