I am trying to write a PL/SQL Script that reads tables and generates a dynamic query based on rule_id
and parameter_id
.
My script should read from parameter_value
based on rule id and parameter id and use parameter value in dynamic query.
So my table called rules
looks like this:
Here is my script - what am I doing wrong? I am getting an error
ORA-01747 invalid user.table.column,table.column or column specification
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;
Well... debug and you should find what is wrong.
dbms_output.put_line
(or a logging framework like logger) to instrument your code.Enjoy the debugging process below !
Manually create sample data since poster provided a screenshot. Please provide this code yourself next time - this is be your job not ours.
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 the code above:
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.
fix error on line 28, run block
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:
fix error on line 6, run block
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:
fix error on line 8, run block
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:
replace occurrences of PARAM_ID with PARAMETER_ID, run block
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:
ah... we got the error !
This is the code that gives the original error:
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;
/
Now it's time to do the proper debugging. Comment out the EXECUTE IMMEDIATE v_query;
and replace add dbms_output.put_line(v_query);
to see what you're trying to execute. Result: lots of rows like:
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...
The sql statements are (1) incomplete, (2) keywords are concatenated and there are way too many rows. Both inner loop and outer loop do a full select.
...some work...
Final solution:
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
This will execute successfully if all tables in each select statement actually exist in the database.
What if you could get your queries without PL/SQL - just plain SQL?
Lets say that your two tables look like below:
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
);
... and that your rules are set up like here
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
);
If we Pivot And Unpivot the rules using a CTE (named params)_
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
Resulting dataset has everything you need to construct different sql commands. Here for RULE_ID = 1 there will be SQL for left joining tables and selecting rows that doesn't match. For RULE_ID = 2 rows that does match.
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
*/
The first query, if run against above sample data, results as:
-- COL_1_T1 CNT
-- -------- ----------
-- Z 1
... while second results as:
-- COL_1_T1 CNT
-- -------- ----------
-- X 2
-- Y 1
You can select some or all of other columns and you can construct the sql commands with different joins and where conditions, groupings, orderings etc...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.