簡體   English   中英

基於多行的PL/SQL動態查詢

[英]PL/SQL dynamic query based on multiple rows

我正在嘗試編寫一個 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;

好吧...調試,您應該會發現問題所在。

  • 對所有未來堆棧溢出帖子的提示:在粘貼之前檢查並運行您的代碼
  • 第二個提示:寫幾行,然后測試,如果需要修復,然后繼續寫幾行。 否則,您最終會遇到大量錯誤,並且會失去概覽。
  • 第三個提示:使用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:\n%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:\n%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:\n%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:\n%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; 並替換添加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) 關鍵字連接在一起,行數太多。 內循環和外循環都做一個完整的 select。

...一些工作...

最終解決方案:

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 語句中的所有表都實際存在於數據庫中,這將成功執行。

如果您可以在沒有 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
      );

如果我們 Pivot 並使用 CTE(命名參數)反透視規則_

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

您可以 select 一些或所有其他列,您可以構造具有不同連接和條件、分組、排序等的 sql 命令...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM