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;
CodePudding user response:
Well... debug and you should find what is wrong.
- Tip for all future stack overlow posts: check AND RUN your code before you paste it.
- 2nd Tip: write a couple of lines, then test, fix if needed, then continue with some lines. Else you'll end up with an large number of errors and you'll lose overview.
- 3rd Tip: use
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.
CodePudding user response:
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...