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

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!

Related

How to delete duplicate rows from all of my oracle database tables, using PL/SQL?

A generic way to remove perfectly duplicated rows from all tables owned by a certain user.
Here's the stored procedure for it. Don't forget to replace 'YOUR_USERNAME_HERE' with your username.
CREATE OR REPLACE PROCEDURE DELETE_DUPLICATES_FROM_DB
IS
TABLE_COLUMNS VARCHAR2(10000);
DELETE_STATEMENT VARCHAR2(10000);
CURSOR ALL_MY_TABLES IS SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = 'YOUR_USERNAME_HERE';
BEGIN
FOR MY_TABLE IN ALL_MY_TABLES
LOOP
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY 1) INTO TABLE_COLUMNS FROM USER_TAB_COLUMNS WHERE TABLE_NAME = MY_TABLE.TABLE_NAME;
DELETE_STATEMENT := 'DELETE FROM ' || MY_TABLE.TABLE_NAME || ' WHERE ROWID NOT IN (SELECT MIN(ROWID) FROM ' || MY_TABLE.TABLE_NAME || ' GROUP BY ' || TABLE_COLUMNS || ');';
EXECUTE IMMEDIATE DELETE_STATEMENT;
DBMS_OUTPUT.PUT_LINE(DELETE_STATEMENT);--print the statements we run
END LOOP;
END;

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.

Nested Table and EXECUTE IMMEDIATE

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));

Point me in the right direction

-- File: PLh10.sql
-- Author: John Tunisi
-- ----------------------------------
SET SERVEROUTPUT ON
SET VERIFY OFF
-- ----------------------------------
ACCEPT traineeID NUMBER PROMPT 'Enter a trainee ID: '
ACCEPT increment NUMBER PROMPT 'Enter an increment for his trainers: '
DECLARE
sr sailors%ROWTYPE;
CURSOR tCursor IS
SELECT S.sid, S.sname, S.rating, S.age, S.trainee
FROM sailors S, sailors R
WHERE R.sid = '&traineeID' AND
S.trainee = R.sid;
BEGIN
OPEN tCursor;
LOOP
-- Fetch the qualifying rows one by one
FETCH tCursor INTO sr;
-- Print the sailor' old record
DBMS_OTPUT.PUT_LINE ('+++++ old row: '||sr.sid||' '
||sr.sname||sr.rating||' '||sr.age||' '||sr.trainee);
-- Increment the trainers' rating
sr.rating := sr.rating + &increment;
UPDATE sailors
SET rating = sr.rating
WHERE sailors.sid = sr.sid;
-- Print the sailor' new record
DBMS_OUTPUT.PUT_LINE ('+++++ new row: '||sr.sid||' '
||sr.sname||sr.rating||' '||sr.age||' '||sr.trainee);
END LOOP;
IF tCursor%ROWCOUNT = 0 /*test whether the trainee has no trainers*/
DBMS_OUTPUT.PUT_LINE ('+++++ '||sr.sid||' is either not a sailor,'
||' or has no trainer');
ELSE
DBMS_OUTPUT.PUT_LINE ('+++++ DB has been updated');
END IF;
CLOSE tCursor;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('+++++'||SQLCODE||'...'||SQLERRM);
END;
/
-- Let's see what happened to the database
SELECT *
FROM sailors S
WHERE S.trainee = '&traineeID';
UNDEFINE traineeID
UNDEFINE increment
Okay, so I need to increment trainers based on a trainee. I think most of it is correct, but I am getting an error on the line below " DBMS_OUTPUT.PUT_LINE ('+++++ '||sr.sid||' is either not a sailor,'". I am not sure what is supposed to go here, as this is my first time writing PL/SQL.
Try this:
SET SERVEROUTPUT ON
SET VERIFY OFF
-- ----------------------------------
ACCEPT traineeID NUMBER PROMPT 'Enter a trainee ID: '
ACCEPT increment NUMBER PROMPT 'Enter an increment for his trainers: '
DECLARE
sr sailors%ROWTYPE;
srNew sailors%ROWTYPE;
nRecords_updated NUMBER := 0;
CURSOR tCursor IS
SELECT S.sid, S.sname, S.rating, S.age, S.trainee
FROM sailors S, sailors R
WHERE R.sid = '&traineeID' AND
S.trainee = R.sid;
BEGIN
OPEN tCursor;
LOOP
-- Fetch the qualifying rows one by one
FETCH tCursor INTO sr;
EXIT WHEN tCursor%NOTFOUND; -- ADDED
-- Print the sailor' old record
DBMS_OUTPUT.PUT_LINE ('+++++ old row: ' || sr.sid || ' ' ||
sr.sname || sr.rating || ' ' || sr.age ||
' ' || sr.trainee);
-- Increment the trainers' rating
sr.rating := sr.rating + &increment;
UPDATE sailors
SET rating = sr.rating
WHERE sailors.sid = sr.sid;
nRecords_updated := nRecords_updated + SQL%ROWCOUNT; -- ADDED
-- Obtain the updated record -- ADDED
SELECT s.* -- ADDED
INTO srNew -- ADDED
FROM SAILORS s -- ADDED
WHERE s.SID = sr.SID; -- ADDED
-- Print the sailor' new record
DBMS_OUTPUT.PUT_LINE ('+++++ new row: ' || srNew.sid || ' ' || -- CHANGED
srNew.sname || srNew.rating || ' ' || -- CHANGED
srNew.age || ' ' || srNew.trainee); -- CHANGED
END LOOP;
IF nRecords_updated = 0 /*test whether the trainee has no trainers*/ -- CHANGED
DBMS_OUTPUT.PUT_LINE ('+++++ ' || sr.sid || ' is either not a sailor,' ||
' or has no trainer');
ELSE
DBMS_OUTPUT.PUT_LINE ('+++++ DB has been updated');
END IF;
CLOSE tCursor;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('+++++'||SQLCODE||'...'||SQLERRM);
END;
/
I included comments (either ADDED or CHANGED) on the lines I altered or added. It's tough to say whether this will work or not as I don't have access to your data but it might be a step in the right direction.
Share and enjoy.

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