Nested Table and EXECUTE IMMEDIATE - plsql

I get all the time an problem with the following snipped from my PL/SQL procedure
I need the variables to create an dynamic SQL statement, but i also need the nested table.
l_code list_code := list_code();
l_code := PKG_DATA.GET_CODES(in_code) ;
v_sql := 'SELECT ' || in_var_name ||'
FROM view
WHERE supplier = '||in_supplier ||'
AND factory = '|| in_factory ||'
AND code IN ('|| ( SELECT * FROM TABLE(l_code) )||') ';
EXECUTE IMMEDIATE v_sql
INTO v_value;
v_value is an nested table
[Error] PLS-00103 (60: 46): PLS-00103: Encountered the symbol "SELECT"
when expecting one of the following:
( - + case mod new not null continue, [Error] PLS-00103
(60: 74): PLS-00103: Encountered the symbol ")" when expecting one of
the following:
, ; for
as group having intersect minus order start union wh
Does anyone have an idea or an snipped ?
thx

v_sql := 'SELECT ' || in_var_name
|| ' FROM view '
|| ' WHERE supplier = :supplier AND factory = :factory '
|| ' AND code IN (SELECT * FROM TABLE(PKG_DATA.GET_CODES(:code)))';
EXECUTE IMMEDIATE v_sql INTO v_value USING in_supplier, in_factory, in_code;

I suggest using bind variables
v_sql := 'SELECT :in_var_name
FROM view
WHERE supplier = :in_supplier
AND factory = :in_factory
AND code IN (SELECT * FROM TABLE(:l_code) )) ';
EXECUTE IMMEDIATE v_sql
INTO v_value
USING in_var_name, in_supplier, in_factory, l_code;
But chances are, that you don't need dynamic SQL at all:
SELECT in_var_name
INTO v_value
FROM view
WHERE supplier = in_supplier
AND factory = in_factory
AND code IN (SELECT * FROM TABLE(l_code));

Related

How to use Insert query within for loop in PLSQL using implicit cursor

I have an existing table and I want to add some entry in another table for each row of the given first table.
I am writing my PLSQL command as:
BEGIN
FOR record in (select cola_guid, hapc_guid, tar_guid from tabA) LOOP
select count(*) INTO v_record_exists
from p where
p.cola_guid = record.cola_guid;
IF v_record_exists = 0 THEN
execute immediate 'insert into NTABLE (tar_guid, PC_NAE, PCV) values (record.tar_guid, ' || '''abcd''' || ', ' || '''val1''' || ')';
ELSE
execute immediate 'insert into NTABLE (tar_guid, PC_NAE, PCV) values (record.tar_guid, ' || '''abcd''' || ', ' || '''val2''' || ')';
END IF;
execute immediate 'insert into NTABLE (tar_guid, PC_NAE, PCV) values (record.tar_guid, ' || '''RA_hapc_guid''' || ', record.hapc_guid)';
execute immediate 'insert into NTABLE (tar_guid, PC_NAE, PCV) select record.tar_guid, PC_NAE, PCV from p where record.cola_guid = p.cola_guid and PC_NAE = ' || '''propVal''' || ' ';
END LOOP;
END;
Now I am getting error:
ORA-00984: column not allowed here
in line:
execute immediate 'insert into NTABLE (tar_guid, PC_NAE, PCV) values (record.tar_guid, ' || '''abcd''' || ', ' || '''val1''' || ')';
I am new to PLSQL world but I really tried triaging and googling but wasn't able to resolve. Please guide and help.
There is no need for you to use dynamic sql here - you know all the columns and tables you're inserting/selecting from, so you can simply use the PL/SQL variables directly in the SQL statement.
Also, when you're writing SQL inside PL/SQL, for performance reasons (as well as easy to read, maintain and debug) you should think set based.
It's entirely possible to do all your inserts in a single insert statement, which you can put inside a procedure.
BEGIN
INSERT INTO ntable (tar_guid, pc_nae, pcv)
WITH results AS (SELECT t.cola_guid,
t.hapc_guid,
t.tar_guid,
CASE WHEN EXISTS (SELECT NULL FROM p WHERE p.cola_guid = t.cola_guid) THEN 'val1' ELSE 'val2' END val
FROM taba t)
SELECT tar_guid,
'abcd' pc_nae,
val pcv
FROM results
UNION ALL
SELECT tar_guid,
'RA_hapc_guid' pc_nae
hapc_guid pcv
FROM results
UNION ALL
SELECT p.tar_guid,
p.pc_nae,
p.pcv
FROM results r
inner JOIN p ON r.cola_guid = p.cola_guid
WHERE p.pc_nae = 'propVal';
END;
/
Don't forget you'll need to commit/rollback as required!

Returning table results from a dynamic PL/SQL query

Trying to dynamically generate and execute a PL/SQL statement. It doesn't return results, but executing okay. What I am trying is to get the table name from the schema in first statement (got it right!), and append that to a select statement, and then execute it to return the table results.
DECLARE
LATEST_TABLE VARCHAR2(256);
PostalCode ADM.POSTAL_CODE_201801%ROWTYPE;
BEGIN
SELECT TO_CHAR(max(table_name)) INTO LATEST_TABLE FROM all_tables WHERE owner = 'ADM' AND table_name LIKE 'POSTAL_CODE_%';
LATEST_TABLE := 'begin Select POSTALCODE,LONGITUDE,LATITUDE,MUNICIPALITY_FULL_NAME,LOCAL_NAME,SZONE_NAME,ZONE_NAME,RHA_CODE,RHA_NAME,URBAN,ZONE_RURAL from ADM.'||LATEST_TABLE||' ;end;';
execute immediate LATEST_TABLE into PostalCode;
Exception
When others then
Null;
END;
Why am I not getting any results? Adding
dbms_output.put_line(PostalCode.LONGITUDE || PostalCode.LATITUDE); after execute immediate is also not generating results!
I see a couple of issues here; your code is something like:
declare
vSQL varchar2(1000);
vTabName varchar2(30);
vResult number;
begin
select table_name into vTabName from user_tables;
vSQL := 'begin select a from ' || vTabName || '; end;';
execute immediate vSQL into vResult;
dbms_output.put_line('REsult: ' || vResult);
exception
when others then
null
end;
If you run this, you see nothing, because the dynamic part gives error, but the (dangerous) exception handling hides it; if you would edit the null; into something like
dbms_output.put_line('Error: ' || sqlerrm);
you would get:
Error: ORA-06550: line 1, column 7:
PLS-00428: an INTO clause is expected in this SELECT statement
In fact you dynamic code is like
begin select a from someTable; end;
and this gives error.
A way to do what you need could be:
...
vSQL := 'select a from ' || vTabName;
execute immediate vSQL into vResult;
...

Aliasing in Bulk Collect giving error of unimplemented feature

The problem statement: The user would specify a name based on which I have to pull names of two tables from a table and then extract values from those tables.I have created a pl/sql procedure for that and since the select query can return n number of rows I'm using Bulk Collect. I have created and object based on the fields I want to extract. Now the problem is that the columns are common in both the tables, so if I don't use alias I get ambiguous column error and if I use that I get the error of unimplemented feature.
here's my code:
create or replace type recon_obj_vib
is object (RECON_TABLE_KEY NUMBER(19)
,RECON_CHGLOGATTR_IDXLST VARCHAR2(1000 CHAR));
create or replace type recon_tab_vib
is table of recon_obj_vib;
create or replace PROCEDURE noMatchReport_proc(tableDesc IN VARCHAR2)
IS
l_recon_tab_vib recon_tab_vib := recon_tab_vib();
n Integer :=0;
out varchar2(2000);
tableName1 varchar2(25);
tableName2 varchar2(25);
tableDesc_without_space varchar2(25);
tableDesc_ra varchar2(25);
BEGIN
tableDesc_without_space:=Regexp_Replace(tableDesc,'\s');
tableDesc_ra:=UPPER('RA_' || tableDesc_without_space || ' %');
out:= 'Select recon_table_name from recon_tables where recon_table_desc = (:value) and rownum=1 and RECON_TABLE_name like (:userName)';
execute immediate out into tableName1 USING tableDesc,tableDesc_ra;
out:= 'Select recon_table_name from recon_tables where recon_table_desc = (:value) and rownum=1 and RECON_TABLE_name not like (:userName)';
execute immediate out into tableName2 USING tableDesc,tableDesc_ra;
out:='Select a.RECON_TABLE_KEY,a.RECON_CHGLOGATTR_IDXLST BULK COLLECT INTO l_recon_tab_vib from ' || tableName1 || ' a , ' || tableName2 || ' b where a.RE_KEY = b.RE_KEY and rownum=1';
execute immediate out into l_recon_tab_vib;
FOR i IN 1..l_recon_tab_vib.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('RECON_TABLE_KEY '|| l_recon_tab_vib(i).RECON_TABLE_KEY ||' RECON_CHGLOGATTR_IDXLST ' || l_recon_tab_vib(i).RECON_CHGLOGATTR_IDXLST );
END LOOP;
END;
This part:
out:='Select a.RECON_TABLE_KEY,a.RECON_CHGLOGATTR_IDXLST BULK COLLECT INTO l_recon_tab_vib from ' || tableName1 || ' a , ' || tableName2 || ' b where a.RE_KEY = b.RE_KEY and rownum=1';
execute immediate out into l_recon_tab_vib;
Should be:
out:='Select a.RECON_TABLE_KEY,a.RECON_CHGLOGATTR_IDXLST from '
|| tableName1 || ' a , ' || tableName2
|| ' b where a.RE_KEY = b.RE_KEY and rownum=1';
execute immediate bulk collect into l_recon_tab_vib;
i.e. the BULK COLLECT INTO clause is part of the calling PL/SQL not part of the dynamic SQL.

Compilation error with plsql procedure

Below is a plsql procedure which was written by someone else and I have asked to fix its compilation errors and run it right.
PROCEDURE DropMyTable IS
x number;
TYPE cur_typ IS REF CURSOR;
--type NAME_TBL is table of VARCHAR2(30);
--tab_Table NAME_TBL;
tab_Table VARCHAR2S(30);
stmt VARCHAR2(4096);
stmt2 VARCHAR2(4096);
outerCur cur_typ;
rows NATURAL := 1000;
TABLE_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT(TABLE_DOES_NOT_EXIST, -942);
BEGIN
stmt2 := 'select distinct G_Tab from G_SERVE where G_TYPE=''CAP''';
--Begin
OPEN outerCur FOR stmt2;
LOOP
FETCH outerCur BULK COLLECT INTO
tab_Table LIMIT rows;
EXIT WHEN tab_Table.COUNT = 0;
FOR i IN 1..tab_Table.COUNT LOOP
--------------------------------------------------------------------
-- Drop the tables in G_SERVE.G_TAB
DBMS_OUTPUT.PUT_LINE('*** Drop table: ' || tab_Table(i) || ' ***');
-- BEGIN
--First drop the optable
stmt := 'DROP TABLE ' || tab_Table(i) || '_OPTAB CASCADE CONSTRAINTS';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE stmt;
--drop the base table
stmt := 'DROP TABLE ' || tab_Table(i) || ' CASCADE CONSTRAINTS';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE stmt;
--drop the Package
stmt := 'DROP PACKAGE ' || tab_Table(i) || '_PKG';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE stmt;
EXCEPTION
-- When the table, optab and package doesn't exist,
-- ignore error "ORA-00942: table, optab or package does not exist".
WHEN TABLE_DOES_NOT_EXIST THEN
DBMS_OUTPUT.PUT_LINE('table, optab or package does not exist');
DBMS_OUTPUT.PUT_LINE(CHR(9));
--Re-raises any other errors.
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error!');
RAISE;
END;
END LOOP;
END LOOP;
--END;
END DropMyTable;
First up I have commented out 4th and 5th line of code from begging and changed it to what it looks like now ( 6th line from the top).
Other thing I have commented out 2 BEGIN keywords first commented out "BEGIN" can be seen after
stmt2 := 'select distinct gao_table from GAO_SERVICESIFACE where GAO_TYPE=''CAPABILITY''';
and the second "BEGIN" keyword has been commented out after the following line of code.
DBMS_OUTPUT.PUT_LINE('*** Drop table: ' || tab_Table(i) || ' ***');
Since I suspect that whoever has written this code put unnecessary "BEGIN" keyword (i apologize if I'm wrong as I'm not a plsql expert. But doing all the commenting out thing i got rid of so many errors).
But when I compile the above code I still get following 2 errors.
1)
ORA-06550: line 27, column 7:
PLS-00103: Encountered the symbol "EXCEPTION" when expecting one of the following:
;
( begin case declare end exit for goto if loop mod null
pragma raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
2)
ORA-06550: line 38, column 9:
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.
*Action:
You have commented out the BEGIN inside the inner loop but not its associated EXCEPTION and END. You need that begin. Also you cannot change an array to a scalar variable while the code still expects an array (references to tab_Table(i) etc.)
Decent indentation of code always makes it easier to follow:
PROCEDURE DropMyTable IS
x number;
TYPE cur_typ IS REF CURSOR;
type NAME_TBL is table of VARCHAR2(30);
tab_Table NAME_TBL;
--tab_Table VARCHAR2S(30);
stmt VARCHAR2(4096);
stmt2 VARCHAR2(4096);
outerCur cur_typ;
rows NATURAL := 1000;
TABLE_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT(TABLE_DOES_NOT_EXIST, -942);
BEGIN
stmt2 := 'select distinct G_Tab from G_SERVE where G_TYPE=''CAP''';
--Begin
OPEN outerCur FOR stmt2;
LOOP
FETCH outerCur BULK COLLECT INTO
tab_Table LIMIT rows;
EXIT WHEN tab_Table.COUNT = 0;
FOR i IN 1..tab_Table.COUNT LOOP
--------------------------------------------------------------------
-- Drop the tables in G_SERVE.G_TAB
DBMS_OUTPUT.PUT_LINE('*** Drop table: ' || tab_Table(i) || ' ***');
BEGIN
--First drop the optable
stmt := 'DROP TABLE ' || tab_Table(i) || '_OPTAB CASCADE CONSTRAINTS';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE stmt;
--drop the base table
stmt := 'DROP TABLE ' || tab_Table(i) || ' CASCADE CONSTRAINTS';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE stmt;
--drop the Package
stmt := 'DROP PACKAGE ' || tab_Table(i) || '_PKG';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE stmt;
EXCEPTION
-- When the table, optab and package doesn't exist,
-- ignore error "ORA-00942: table, optab or package does not exist".
WHEN TABLE_DOES_NOT_EXIST THEN
DBMS_OUTPUT.PUT_LINE('table, optab or package does not exist');
DBMS_OUTPUT.PUT_LINE(CHR(9));
--Re-raises any other errors.
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error!');
RAISE;
END;
END LOOP;
END LOOP;
--END;
END DropMyTable;
I have reinstated that BEGIN and restored the array. I don't know if there is anything else wrong.

Duplicate INSERT record procedure

I have problem with mu procedure which insert duplicate last record in table
example when I put INSERT..... 'AAA' I got to rows in table 'AAA' and 'AAA'
In place when I put DBMS()... in code I got tow records
I use trigger and sequence for column ID in HistoriaDismissDate but they are in good condition. I check if I dropped trigger and sequence and its the same situation
I also use viewDate but this view get mi ONE record not two
my code
CREATE OR REPLACE PROCEDURE ChangeDismissDate
IS
v_id VARCHAR2(11);
v_dateBhd DATE := TO_DATE('20491231','yyyymmdd');
v_dateDismiss DATE := TO_DATE('20491231','yyyymmdd');
v_login VARCHAR2(50);
last_id NUMBER :=0;
CURSOR cur IS
select EMP_NO, LOGIN, ODEJSCIE_BHD, ODEJSCIE_OLD FROM viewDate;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO v_id,v_login,v_dateBhd,v_dateDismiss;
DBMS_OUTPUT.put_line(v_id || ' ' || v_login || ' ' || v_dateBhd || ' ' || v_dateDismiss);
UPDATE employee_tab SET DISMISS_DATE = v_dateBhd WHERE EMP_NO = v_id;
COMMIT;
INSERT INTO HistoriaDismissDate(CUSTOMER_ID,LOGIN, DATE_CHANGE, DATE_BHD, DATE_DISMISS)
VALUES(v_id,v_login, sysdate, v_dateBhd, v_dateDismiss);
COMMIT;
EXIT WHEN cur%NOTFOUND;
END LOOP;
CLOSE cur;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_stack);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_stack);
END;
/
2 tips on your original code:
1) cursor is a very old programming technic on PL/SQL. Prefer to use FOR ... LOOP construction. It's cleaner and less error-prone! See how it works:
CREATE OR REPLACE PROCEDURE ChangeDismissDate IS
BEGIN
for cur in (select EMP_NO, LOGIN, ODEJSCIE_BHD, ODEJSCIE_OLD FROM viewDate) loop
DBMS_OUTPUT.put_line(cur.EMP_NO || ' ' || cur.login || ' ' || cur.ODEJSCIE_BHD || ' ' || cur.ODEJSCIE_OLD);
UPDATE employee_tab
SET DISMISS_DATE = cur.ODEJSCIE_BHD
WHERE EMP_NO = cur.EMP_NO;
INSERT INTO HistoriaDismissDate
( CUSTOMER_ID,LOGIN, DATE_CHANGE, DATE_BHD, DATE_DISMISS )
VALUES
( cur.EMP_NO, cur.LOGIN, sysdate, cur.ODEJSCIE_BHD, cur.ODEJSCIE_OLD, );
end loop;
end;
/
2) Never, I mean never put a commit inside your procedure. The commit should be done on the caller block or on your client-side app. When you put a commit inside your procedure, you miss the chance to rollback after running it and other procedures could not call it if they want to control the transaction flow.

Resources