在 where 子句中使用 case when 的 Oracle 存储过程

Oracle stored procedure with case when in the where cause

以下存储过程符合错误。错误似乎在 where cause 的 case 语句中。我该如何解决?

create or replace PROCEDURE APPEND_HIST_TBLS_PROC AS 
CurTerm varchar2(4) := '1222';
PS_ACAD_PROG_HISTORY varchar2(35) := 'PS_ACAD_PROG_HISTORY_' || CurTerm;
Begin
execute immediate 'insert into ' || PS_ACAD_PROG_HISTORY
|| '(select sysdate as date_created,
EFFDT, 
ADMIT_TERM, 
EXP_GRAD_TERM, 
CAMPUS
from ERP_ACAD
where CASE WHEN SUBSTR(ADMIT_TERM,4,1)= '6' THEN SUBSTR(ADMIT_TERM,1,3) || '9' ELSE ADMIT_TERM END = '||CurTerm ||'
)';
END APPEND_HIST_TBLS_PROC;

似乎是引用问题。你试过这个吗:

create or replace PROCEDURE APPEND_HIST_TBLS_PROC AS 
CurTerm varchar2(4) := '1222';
PS_ACAD_PROG_HISTORY varchar2(35) := 'PS_ACAD_PROG_HISTORY_' || CurTerm;
Begin
execute immediate 'insert into ' || PS_ACAD_PROG_HISTORY
|| '(select sysdate as date_created,
EFFDT, 
ADMIT_TERM, 
EXP_GRAD_TERM, 
CAMPUS
from ERP_ACAD
where CASE WHEN SUBSTR(ADMIT_TERM,4,1)= ''6'' 
THEN CONCAT(SUBSTR(ADMIT_TERM,1,3), ''9'') ELSE ADMIT_TERM END = '||CurTerm ||'
)';
END APPEND_HIST_TBLS_PROC;

假设您想在 admin_term 的子字符串末尾附加 9,也使用 CONCAT 而不是双管道。

真的取决于您的数据和您尝试实现的逻辑。此过程现在应该可以编译,但它是否达到预期结果 - 取决于您要在此处实现的逻辑。

动态的SQL很难维护和调试。过程编译成功这一事实告诉 没有任何关于动态语句本身的信息 - 它只是说过程“按原样”没有错误。

编写语句并将其存储到局部变量中然后显示其内容是一个好习惯;确认无误后执行。

此外,由于您必须 转义 单引号(有时它变得令人讨厌,有几个连续的单引号可以满足您的需要),请使用 q-quoting 机制它可以让你写“正常”的语句(那么单引号真的是单引号)。

像这样:

SQL> CREATE OR REPLACE PROCEDURE append_hist_tbls_proc
  2  AS
  3     curterm               VARCHAR2 (4) := '1222';
  4     ps_acad_prog_history  VARCHAR2 (35) := 'PS_ACAD_PROG_HISTORY_' || curterm;
  5     l_str                 VARCHAR2 (4000);
  6  BEGIN
  7     -- Compose the INSERT statement into a VARCHAR2 local variable so that you'd be able
  8     -- to check whether you did it right or not.
  9     -- Use the q-quoting mechanism as it helps with consecutive single quotes issues
 10     l_str :=
 11           'INSERT INTO '
 12        || ps_acad_prog_history
 13        || q'[ SELECT sysdate as date_created,
 14                      effdt,
 15                      admit_term,
 16                      exp_grad_term,
 17                      campus
 18        FROM erp_acad
 19        WHERE CASE WHEN SUBSTR(admit_term, 4, 1) = '6' THEN
 20                        SUBSTR(admit_term, 1, 3) || '9'
 21                   ELSE ADMIT_TERM
 22              END = ]'
 23        || curterm;
 24
 25     -- FIRST check the command you're about to execute
 26     DBMS_OUTPUT.put_line (l_str);
 27
 28     -- When you verified that it is correct, then comment DBMS_OUTPUT.PUT_LINE call
 29     -- and uncomment EXECUTE IMMEDIATE
 30     -- EXECUTE IMMEDIATE l_str;
 31  END append_hist_tbls_proc;
 32  /

Procedure created.

让我们试试看:

SQL> SET SERVEROUTPUT ON
SQL> EXEC append_hist_tbls_proc;
INSERT INTO PS_ACAD_PROG_HISTORY_1222 SELECT sysdate as date_created,

effdt,
                    admit_term,
                    exp_grad_term,

campus
      FROM erp_acad
      WHERE CASE WHEN SUBSTR(admit_term, 4, 1) = '6'
THEN
                      SUBSTR(admit_term, 1, 3) || '9'
                 ELSE
ADMIT_TERM
            END = 1222

PL/SQL procedure successfully completed.

SQL>

输出看起来很难看(这是在过程中漂亮的代价),但是 - 如果你格式化它 - 它看起来像这样:

INSERT INTO PS_ACAD_PROG_HISTORY_1222
   SELECT SYSDATE AS date_created,
          effdt,
          admit_term,
          exp_grad_term,
          campus
     FROM erp_acad
    WHERE CASE
             WHEN SUBSTR (admit_term, 4, 1) = '6'
             THEN
                SUBSTR (admit_term, 1, 3) || '9'
             ELSE
                ADMIT_TERM
          END = 1222

所以,如果你真的可以执行它(我不能,我没有你的表),那么取消注释 execute immediate 并使用该过程。否则,修改语句。