【问题标题】:PL/SQL dynamic query based on multiple rows基于多行的PL/SQL动态查询
【发布时间】:2023-01-03 04:15:25
【问题描述】:

我正在尝试编写一个 PL/SQL 脚本来读取表并生成基于 rule_idparameter_id 的动态查询。

我的脚本应该根据规则 ID 和参数 ID 从 parameter_value 读取并在动态查询中使用参数值。

所以我的名为 rules 的表看起来像这样:

这是我的脚本 - 我做错了什么?我收到一个错误

ORA-01747 无效的 user.table.column、table.column 或列规范

declare 
v_rule_id number(10);
v_parameter_id number(10);
v_parameter_value varchar2(100);
v_source_table varchar2(100);
v_lookup_table varhcar2(100);
v_source_column varchar2(100);
v_lookup_column varchar2(100);
v_date varhchar2(100);
v_query varchar2(1000);

BEGIN
FOR RL IN (SELECT RULE_ID FROM RULE)
LOOP
FOR PRM IN (SELECT PARAMETER_ID,PARAMETER_VALUE FROM RULE)
LOOP
IF PRM.PARAM_ID = 1 THEN 
v_source_table:= PRM.PARAMETER_VALUE;
ELSIF PRM.PARAM_ID = 2 THEN
V_lookup_table := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAM_ID = 3 THEN
V_source_column := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAM_ID = 4 THEN
V_lookup_column := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAM_ID = 5 THEN
V_date := PRM.PARAMETER_VALUE;
END IF;
v_query := 'SELECT * FROM (SELECT DISTINCT A.' || v_source_column || ', count(*) as count from'|| v_source_table || ' A LEFT JOIN' || V_lookup_table || ' ON A.'||V_source_column ||' = B.'|| V_lookup_column || 'WHERE B.'||V_lookup_table||' IS NULL GROUP BY A.'||V_source_column  ||'ORDER BY 2 DESC' );
EXECUTE IMMEDIATE v_query;
END LOOP;
END LOOP;
END;

【问题讨论】:

  • 你在做什么查询?数据不会在任何地方返回,并且 PL/SQL 不只是将输出返回到控制台以供查看。 EXECUTE IMMEDIATE 通常用于没有返回数据的 DDL 命令,或用于包含专门将数据返回到绑定变量以供进一步处理的动态结构的查询。运行此过程的预期输出或结果是什么?此结构(使用连接而不是绑定变量)也可能容易受到 SQL 注入的攻击。
  • 我只需要使用不同参数值的参数值列动态执行查询。它不必向控制台返回任何内容
  • 可能不是唯一的问题,但您缺少一些空格:在“from”|| v_source_table 中的 from 之后,在“JOIN”|| V_lookup_table 中加入之后,在“'ORDER BY'”中的顺序之前...
  • 我看到了两个问题:一个是你正在读取第二个循环中的所有参数,而不仅仅是规则的参数(最好在第一个循环的选择中添加 distinct),第二个是你缺少一些空白在语句中(例如在 FROM 之后)。如果您执行 SQL,而不是获取行,何必呢?只是为了检查语法是否正确?
  • 而不是 EXECUTE IMMEDIATE v_query; 代码 dbms_output.put_linr(v_query); 。你会知道你做错了什么。确保你的 dbms out 是 ON

标签: sql oracle plsql


【解决方案1】:

好吧...调试,您应该会发现问题所在。

  • 提示对于所有未来的堆栈溢出帖子:在粘贴之前检查并运行您的代码.
  • 第二个提示:写几行,然后测试,如果需要修复,然后继续写一些行。否则,您最终会遇到大量错误,并且会失去概览。
  • 第三个提示:使用dbms_output.put_line(或像 logger 这样的日志记录框架)来检测您的代码。

享受下面的调试过程!

由于海报提供了屏幕截图,因此手动创建示例数据。下次请自己提供此代码 - 这是您的工作,而不是我们的工作。

CREATE TABLE rule (RULE_ID,PARAMETER_ID,PARAMETER_EXPLANATION,PARAMETER_VALUE) AS
SELECT 1,1,'TABLE_1','A' FROM DUAL UNION ALL 
SELECT 1,2,'TABLE_2','B' FROM DUAL UNION ALL 
SELECT 1,3,'COLUMN_1','X' FROM DUAL UNION ALL 
SELECT 1,4,'COLUMN_2','Y' FROM DUAL UNION ALL 
SELECT 1,5,'DATE','20221231' FROM DUAL UNION ALL 
SELECT 2,1,'TABLE_1','C' FROM DUAL UNION ALL 
SELECT 2,2,'TABLE_2','D' FROM DUAL UNION ALL 
SELECT 2,3,'COLUMN_1','Z' FROM DUAL UNION ALL 
SELECT 2,4,'COLUMN_2','Q' FROM DUAL UNION ALL 
SELECT 2,5,'DATE','20221231' FROM DUAL;

Table RULE created.

运行上面的代码:

run anonymous pl/sql block

ORA-06550: line 28, column 299:
PLS-00103: Encountered the symbol ")" when expecting one of the following:

   * & = - + ; < / > at in is mod remainder not rem
   <an exponent (**)> <> or != or ~= >= <= <> and or like like2
   like4 likec between || member submultiset
ORA-06550: line 31, column 5:
PLS-00103: Encountered the symbol "LOOP" when expecting one of the following:

   ;
06550. 00000 -  "line %s, column %s:
%s"
*Cause:    Usually a PL/SQL compilation error.

修复第 28 行的错误,运行块

Error report -
ORA-06550: line 6, column 16:
PLS-00201: identifier 'VARHCAR2' must be declared
ORA-06550: line 0, column 1:
PL/SQL: Compilation unit analysis terminated
06550. 00000 -  "line %s, column %s:
%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:

修复第 6 行的错误,运行块

ORA-06550: line 9, column 8:
PLS-00201: identifier 'VARHCHAR2' must be declared
ORA-06550: line 0, column 1:
PL/SQL: Compilation unit analysis terminated
06550. 00000 -  "line %s, column %s:
%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:

修复第 8 行的错误,运行块

Error report -
ORA-06550: line 17, column 8:
PLS-00302: component 'PARAM_ID' must be declared
ORA-06550: line 17, column 1:
PL/SQL: Statement ignored
06550. 00000 -  "line %s, column %s:
%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:

用 PARAMETER_ID 替换出现的 PARAM_ID,运行块

Error report -
ORA-01747: invalid user.table.column, table.column, or column specification
ORA-06512: at line 29
ORA-06512: at line 29
01747. 00000 -  "invalid user.table.column, table.column, or column specification"
*Cause:    
*Action:

啊...我们得到了错误!

这是给出原始错误的代码:

declare 
v_rule_id number(10);
v_parameter_id number(10);
v_parameter_value varchar2(100);
v_source_table varchar2(100);
v_lookup_table varchar2(100);
v_source_column varchar2(100);
v_lookup_column varchar2(100);
v_date varchar2(100);
v_query varchar2(1000);

BEGIN
FOR RL IN (SELECT RULE_ID FROM RULE)
LOOP
FOR PRM IN (SELECT PARAMETER_ID,PARAMETER_VALUE FROM RULE)
LOOP
IF PRM.PARAMETER_ID = 1 THEN 
v_source_table:= PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 2 THEN
V_lookup_table := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 3 THEN
V_source_column := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 4 THEN
V_lookup_column := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 5 THEN
V_date := PRM.PARAMETER_VALUE;
END IF;
v_query := 'SELECT * FROM (SELECT DISTINCT A.' || v_source_column || ', count(*) as count from'|| v_source_table || ' A LEFT JOIN' || V_lookup_table || ' ON A.'||V_source_column ||' = B.'|| V_lookup_column || 'WHERE B.'||V_lookup_table||' IS NULL GROUP BY A.'||V_source_column  ||'ORDER BY 2 DESC';
EXECUTE IMMEDIATE v_query;
END LOOP;
END LOOP;
END;
/

现在是时候进行适当的调试了。注释掉 EXECUTE IMMEDIATE v_query; 并替换 add dbms_output.put_line(v_query); 以查看您要执行的操作。结果:很多行像:

SELECT * FROM (SELECT DISTINCT A., count(*) as count fromA A LEFT JOIN ON A. = B.WHERE B. IS NULL GROUP BY A.ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A., count(*) as count fromA A LEFT JOINB ON A. = B.WHERE B.B IS NULL GROUP BY A.ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count fromA A LEFT JOINB ON A.X = B.WHERE B.B IS NULL GROUP BY A.XORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count fromA A LEFT JOINB ON A.X = B.YWHERE B.B IS NULL GROUP BY A.XORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count fromA A LEFT JOINB ON A.X = B.YWHERE B.B IS NULL GROUP BY A.XORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count fromC A LEFT JOINB ON A.X = B.YWHERE B.B IS NULL GROUP BY A.XORDER BY 2 DESC

etc...

sql 语句 (1) 不完整,(2) 关键字连接在一起,行数太多。内循环和外循环都进行全选。

...一些工作...

最终解决方案:

set serveroutput on size 999999
clear screen
declare 
v_rule_id number(10);
v_parameter_id number(10);
v_parameter_value varchar2(100);
v_source_table varchar2(100);
v_lookup_table varchar2(100);
v_source_column varchar2(100);
v_lookup_column varchar2(100);
v_date varchar2(100);
v_query varchar2(1000);

BEGIN
FOR RL IN (SELECT RULE_ID FROM RULE)
LOOP
FOR PRM IN (SELECT PARAMETER_ID,PARAMETER_VALUE FROM RULE WHERE rule_id = rl.rule_id)
LOOP
IF PRM.PARAMETER_ID = 1 THEN 
v_source_table:= PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 2 THEN
V_lookup_table := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 3 THEN
V_source_column := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 4 THEN
V_lookup_column := PRM.PARAMETER_VALUE;
ELSIF PRM.PARAMETER_ID = 5 THEN
V_date := PRM.PARAMETER_VALUE;
END IF;
END LOOP;
v_query := 'SELECT * FROM (SELECT DISTINCT A.' || v_source_column || ', count(*) as count from '|| v_source_table || ' A LEFT JOIN ' || V_lookup_table || ' ON A.'||V_source_column ||' = B.'|| V_lookup_column || ' WHERE B.'||V_lookup_table||' IS NULL GROUP BY A.'||V_source_column  ||' ORDER BY 2 DESC' ;
dbms_output.put_line(v_query);
--EXECUTE IMMEDIATE v_query; --uncomment if all tables exist.
END LOOP;
END;
/

SELECT * FROM (SELECT DISTINCT A.X, count(*) as count from A A LEFT JOIN B ON A.X = B.Y WHERE B.B IS NULL GROUP BY A.X ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count from A A LEFT JOIN B ON A.X = B.Y WHERE B.B IS NULL GROUP BY A.X ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count from A A LEFT JOIN B ON A.X = B.Y WHERE B.B IS NULL GROUP BY A.X ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count from A A LEFT JOIN B ON A.X = B.Y WHERE B.B IS NULL GROUP BY A.X ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.X, count(*) as count from A A LEFT JOIN B ON A.X = B.Y WHERE B.B IS NULL GROUP BY A.X ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.Z, count(*) as count from C A LEFT JOIN D ON A.Z = B.Q WHERE B.D IS NULL GROUP BY A.Z ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.Z, count(*) as count from C A LEFT JOIN D ON A.Z = B.Q WHERE B.D IS NULL GROUP BY A.Z ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.Z, count(*) as count from C A LEFT JOIN D ON A.Z = B.Q WHERE B.D IS NULL GROUP BY A.Z ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.Z, count(*) as count from C A LEFT JOIN D ON A.Z = B.Q WHERE B.D IS NULL GROUP BY A.Z ORDER BY 2 DESC
SELECT * FROM (SELECT DISTINCT A.Z, count(*) as count from C A LEFT JOIN D ON A.Z = B.Q WHERE B.D IS NULL GROUP BY A.Z ORDER BY 2 DESC

如果每个 select 语句中的所有表都实际存在于数据库中,这将成功执行。

【讨论】:

    【解决方案2】:

    如果您可以在没有 PL/SQL 的情况下获得您的查询 - 只是简单的 SQL,那会怎么样?
    假设您的两个表如下所示:

    CREATE TABLE
      A_TBL_1 (ID, TXT, SOME_COL, COL_1_T1, DATE_T1) AS
          (
              SELECT 1,   'TEXT for ID 1', 'Something else 1 in tbl_1', 'X',  To_Date('20221231', 'yyyymmdd') From Dual Union All
              SELECT 2,   'TEXT for ID 2', 'Something else 2 in tbl_1', 'Y',  To_Date('20221231', 'yyyymmdd') From Dual Union All
              SELECT 3,   'TEXT for ID 3', 'Something else 3 in tbl_1', 'Z',  To_Date('20221231', 'yyyymmdd') From Dual
          );
    CREATE TABLE     
      A_TBL_2 (ID, TXT, SOME_COL, COL_1_T2) AS
          (
              SELECT 11,   'TEXT for ID 11', 'Something else 11 in tbl_2', 'X' From Dual Union All
              SELECT 12,   'TEXT for ID 12', 'Something else 12 in tbl_2', 'Y' From Dual Union All
              SELECT 13,   'TEXT for ID 13', 'Something else 13 in tbl_2', 'X' From Dual
          );
    

    ...并且您的规则设置如下

    CREATE TABLE
        A_RULE_TBL (RULE_ID, PAR_ID, PAR_EXPL, PAR_VAL) AS
          (
            SELECT 1, 1, 'A_TBL_1',     'a'     FROM DUAL UNION ALL 
            SELECT 1, 2, 'A_TBL2',      'b'     FROM DUAL UNION ALL 
            SELECT 1, 3, 'COL_1_T1',    'X'     FROM DUAL UNION ALL 
            SELECT 1, 4, 'COL_1_T2',    'X'     FROM DUAL UNION ALL 
            SELECT 1, 5, 'DATE_T1',         '20221231' FROM DUAL UNION ALL 
            SELECT 2, 1, 'A_TBL_1',     'a'     FROM DUAL UNION ALL 
            SELECT 2, 2, 'A_TBL_2',     'b'     FROM DUAL UNION ALL 
            SELECT 2, 3, 'COL_1_T1',    'Y'     FROM DUAL UNION ALL 
            SELECT 2, 4, 'COL_1_T2',    'Y'     FROM DUAL UNION ALL 
            SELECT 2, 5, 'DATE_T1',      '20221231' FROM DUAL
          );
    

    如果我们使用 CTE(命名参数)对规则进行 Pivot 和 Unpivot_

    WITH
        params AS
            (   Select    *
                From      A_RULE_TBL 
                PIVOT (
                        Max(CASE WHEN PAR_ID = 1 THEN PAR_EXPL END) "SRC_TBL",
                        Max(CASE WHEN PAR_ID = 2 THEN PAR_EXPL END) "LKP_TBL",
                        Max(CASE WHEN PAR_ID = 3 THEN PAR_EXPL END) "SRC_COL",
                        Max(CASE WHEN PAR_ID = 4 THEN PAR_EXPL END) "LKP_COL",
                        Max(CASE WHEN PAR_ID = 5 THEN PAR_EXPL END) "DATE",
                        --
                        Max(CASE WHEN PAR_ID = 1 THEN PAR_VAL END) "SRC_TBL_VAL",
                        Max(CASE WHEN PAR_ID = 2 THEN PAR_VAL END) "LKP_TBL_VAL",
                        Max(CASE WHEN PAR_ID = 3 THEN PAR_VAL END) "SRC_COL_VAL",
                        Max(CASE WHEN PAR_ID = 4 THEN PAR_VAL END) "LKP_COL_VAL",
                        Max(CASE WHEN PAR_ID = 5 THEN PAR_VAL END) "DATE_VAL"
                        FOR RULE_ID IN(1 "ID1", 2 "ID2")    )
            
                UNPIVOT(  (SRC_TBL, SRC_COL, LKP_TBL, LKP_COL, A_DATE, SRC_TBL_VAL, SRC_COL_VAL, LKP_TBL_VAL, LKP_COL_VAL, DATE_VAL) 
                            FOR RULE_ID
                            IN  (
                                (ID1_SRC_TBL, ID1_SRC_COL, ID1_LKP_TBL, ID1_LKP_COL, ID1_DATE, ID1_SRC_TBL_VAL, ID1_SRC_COL_VAL, ID1_LKP_TBL_VAL, ID1_LKP_COL_VAL, ID1_DATE_VAL ) as 1,
                                (ID2_SRC_TBL, ID2_SRC_COL, ID2_LKP_TBL, ID2_LKP_COL, ID2_DATE, ID2_SRC_TBL_VAL, ID2_SRC_COL_VAL, ID2_LKP_TBL_VAL, ID2_LKP_COL_VAL, ID2_DATE_VAL ) as 2   )
                      )
                ORDER BY RULE_ID
          )
    --  
    --  R e s u l t
    --     RULE_ID SRC_TBL  SRC_COL  LKP_TBL  LKP_COL  A_DATE   SRC_TBL_VAL SRC_COL_VAL LKP_TBL_VAL LKP_COL_VAL DATE_VAL
    --  ---------- -------- -------- -------- -------- -------- ----------- ----------- ----------- ----------- --------
    --           1 A_TBL_1  COL_1_T1 A_TBL_2  COL_1_T2 DATE_T1  a           X           b           X           20221231 
    --           2 A_TBL_1  COL_1_T1 A_TBL_2  COL_1_T2 DATE_T1  a           Y           b           Y           20221231
    

    结果数据集包含构建不同 sql 命令所需的一切。这里对于 RULE_ID = 1 将有用于左连接表和选择不匹配的行的 SQL。对于 RULE_ID = 2 匹配的行。

    SELECT 
        'Select ' || SRC_TBL_VAL || '.' || SRC_COL || ', Count(*) "CNT" ' || Chr(10) ||
        'From ' || SRC_TBL || ' ' || SRC_TBL_VAL || ' ' ||  Chr(10) ||
        'Left Join ' || LKP_TBL || ' ' || LKP_TBL_VAL || ' ON(' || LKP_TBL_VAL || '.' || LKP_COL || ' = ' || SRC_TBL_VAL || '.' || SRC_COL || ')' ||  Chr(10) ||
        'Where ' || LKP_TBL_VAL || '.' || LKP_COL || ' Is ' || CASE RULE_ID WHEN 2 THEN 'Not' ELSE '' END   || ' Null ' ||  Chr(10) ||
        'Group By ' || SRC_TBL_VAL  || '.' || SRC_COL || ' ' ||  Chr(10) ||
        'Order By Count(*) DESC' "SQL_COMMANDS"
    FROM      params
    ORDER BY  RULE_ID
    /*  R e s u l t :
    SQL_COMMANDS                                       
    --------------------------------------------------
    Select a.COL_1_T1, Count(*) "CNT"                 
    From A_TBL_1 a                                    
    Left Join A_TBL_2 b ON(b.COL_1_T2 = a.COL_1_T1)   
    Where b.COL_1_T2 Is  Null                         
    Group By a.COL_1_T1                               
    Order By Count(*) DESC                            
    
    Select a.COL_1_T1, Count(*) "CNT"                    
    From A_TBL_1 a                                       
    Left Join A_TBL_2 b ON(b.COL_1_T2 = a.COL_1_T1)      
    Where b.COL_1_T2 Is Not Null                         
    Group By a.COL_1_T1                                  
    Order By Count(*) DESC                         
    */
    

    如果针对上述示例数据运行第一个查询,结果为:

    --  COL_1_T1        CNT
    --  -------- ----------
    --  Z                 1
    

    ...而第二个结果为:

    --  COL_1_T1        CNT
    --  -------- ----------
    --  X                 2 
    --  Y                 1
    

    【讨论】:

      猜你喜欢
      • 2015-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多