case-when oracle sql的动态评估

Posted

技术标签:

【中文标题】case-when oracle sql的动态评估【英文标题】:dynamic evaluation of case-when oracle sql 【发布时间】:2021-09-30 21:29:03 【问题描述】:

我想知道是否可以在 Oracle SQL 的 select 语句中动态评估 case 语句。以下面的 sql代码为例:

WITH temp AS (
    SELECT
        'WHEN ' || when_col || ' THEN ''' || then_value || '''' as case_logic
    FROM some_table
)
SELECT
    case
        temp.case_logic -- EVALUATE THIS FOR ALL ROWS IN TEMP DURING RUNTIME
        else 'NA'
    end as case_when,
    table2.*
FROM table2;

我尝试使用占位符实现上述内容,如下所示:

DECLARE
    case_when_logic VARCHAR2(4000 byte);
    plsql_block VARCHAR2(4000 byte);
    
    TYPE case_when_logic_tbl_type IS TABLE OF VARCHAR2(1000 BYTE);
    case_when_logic_tbl case_when_logic_tbl_type;

BEGIN
    
    case_when_logic := q'[SELECT 'WHEN ' | | when_condition_col | | ' THEN ''' | | then_condition_col | | '''' as case_when FROM some_table]';

    execute immediate case_when_logic BULK COLLECT INTO case_when_logic_tbl;

    plsql_block := q'[SELECT CASE :case_when
                                  ELSE 'some other value'
                             END case_when_col,
                             table2.*
                        FROM table2]';

    EXECUTE IMMEDIATE plsql_block USING case_when_logic_tbl;
END;
/

但不幸的是,这会引发错误:

PLS-00457: expressions have to be of SQL types

有没有办法做到这一点?请提供代码中的示例,因为我没有看到类似的解决方案。

【问题讨论】:

请edit 您的问题包含minimal reproducible example 和:CREATE TABLE 表的声明; INSERT 样本数据声明;以及该样本数据的预期输出。 【参考方案1】:

立即执行中的 USING 子句采用位置表示法 (docs) 中的绑定变量。所以你不能传递一个关联数组。而且您只能传递有效的绑定变量(解释为here)。没有表名、子句等。

您可以连接 select 语句并执行以下操作:

DECLARE
    case_when_logic VARCHAR2(4000 byte);
    plsql_block VARCHAR2(4000 byte);
    
    TYPE t_emp_tab IS TABLE OF emp.ename%TYPE;
    l_ename_tab t_emp_tab;

BEGIN

    case_when_logic := q'[WHEN ENAME = 'KING' THEN 'BOSS' ELSE 'NOBODY']';

    plsql_block := q'[SELECT
                        CASE ]'||case_when_logic||q'[
                        END                         
                    FROM
                        emp WHERE ename = :name]';

    EXECUTE IMMEDIATE plsql_block BULK COLLECT INTO l_ename_tab USING 'KING';
    
    FOR r IN 1 .. l_ename_tab.COUNT LOOP
       dbms_output.put_line(l_ename_tab(r));
    END LOOP;
END;
/

如果您必须循环遍历一个数字 if WHEN x THEN y 子句,则连接您的语句。传递给EXECUTE IMMEDIATE时语句必须完整

DECLARE
    plsql_block VARCHAR2(4000 byte);
    
    TYPE vc_tab IS TABLE OF VARCHAR2(4000);
    l_case_when_logic_tab vc_tab;
    
    TYPE t_emp_tab IS TABLE OF emp.ename%TYPE;
    l_ename_tab t_emp_tab;

BEGIN

    l_case_when_logic_tab := vc_tab
      (q'[WHEN JOB = 'PRESIDENT' THEN 'BOSS' ]',
       q'[WHEN JOB = 'MANAGER' THEN 'TRYHARD' ]',
       q'[WHEN JOB = 'SALESMAN' THEN 'STORYTELLER' ]'
      );

    plsql_block := q'[SELECT CASE ]';
    FOR i IN l_case_when_logic_tab.FIRST .. l_case_when_logic_tab.LAST LOOP
      plsql_block := plsql_block || l_case_when_logic_tab(i);
    END LOOP;
    plsql_block := plsql_block ||q'[
                        END                         
                    FROM
                        emp WHERE ename = :name]';
    
    dbms_output.put_line('statement: ');                       
    dbms_output.put_line(plsql_block);                            

    EXECUTE IMMEDIATE plsql_block BULK COLLECT INTO l_ename_tab USING 'KING';
    
    FOR r IN 1 .. l_ename_tab.COUNT LOOP
       dbms_output.put_line(l_ename_tab(r));
    END LOOP;
END;
/

【讨论】:

有没有办法将关联数组传递给plsql_block?我有大量的 when-then 条件被收集到 case_when_logic 中(参见我的第二个示例)。 更新了我的答案

以上是关于case-when oracle sql的动态评估的主要内容,如果未能解决你的问题,请参考以下文章

sql问题--case-when

SQL Select CASE-WHEN - 如何从电话号码中删除格式

SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估

在 Oracle 触发器中动态评估伪记录 (:OLD, :NEW)

为啥没有在动态 SQL 中评估数学条件?

sql 删除动态波形中的重复评估