PLSQL - Cursor cannot be used in dynamic sql - plsql

I have a dynamic SQL query which is constructed in a string.
The procedure should return a 'REF CURSOR'.
I get the error PLS-00455 when I try to open the cursor for the query.
Cursor definition
CURSOR cu_SiteList IS
SELECT SEC_NN.SRV_ID
,SEC_NN.SRV_NAME
,SEC_NN.SRV_COMTYP_CODE
FROM SEC_NN
,COM_SITE_STATE_T
WHERE SEC_NN.SRV_COMTYP_CODE <> 1
AND SEC_NN.SRV_ID = 2;
TYPE SITE_LIST_TYP IS REF CURSOR RETURN cu_SiteList%ROWTYPE;
Here is the query:
p_SiteList SITE_LIST_TYP;
lv_QueryStr := ' SELECT SEC_NN.SRV_ID ' ||
' ,SEC_NN.SRV_NAME ' ||
' ,SEC_NN.SRV_COMTYP_CODE ' ||
' FROM SEC_NN_, ' ||
' COM_SITE_STATE_T ' ||
' WHERE SEC_NN.SRV_COMTYP_CODE <> 1 ' ||
' AND SEC_NN.SRV_MODE_CODE = 2' ||
' AND SEC_NN.SRV_ID = COM_SITE_STATE_T.SRV_ID';
OPEN p_SiteList FOR lv_QueryStr;
As you can see I only use 3 columns from SEC_NN table, so creating a cursor that is a ROWTYPE of the entire table will not work for me.
How can overcome this?
Thanks in advance.

From the comments, the fix is to declare the cursor as a 'weak' ref cursor, by replacing the line
p_SiteList SITE_LIST_TYP;
with
p_SiteList SYS_REFCURSOR;

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!

Rename column if it has single quote

I need to rename column in my oracle database using pl/sql. For example you have table like this.
CREATE TABLE TEST(
id int,
"'test1" varchar(80),
"test2" varchar(80)
);
and you need to remove all quotes from it. I wrote anonymous block and there is the problem:
...
FOR column_rec IN (SELECT column_name FROM USER_TAB_COLUMNS WHERE TABLE_NAME=table_rec.TABLE_NAME) LOOP
new_column_name := column_rec.COLUMN_NAME;
new_column_name := REPLACE(new_column_name, chr(34), '');
new_column_name := REPLACE(new_column_name, chr(39), '');
...
EXECUTE IMMEDIATE
'ALTER TABLE '
|| table_rec.TABLE_NAME
|| ' RENAME COLUMN '
|| column_rec.COLUMN_NAME
|| ' TO '
|| new_column_name;
...
But if column_rec.COLUMN_NAME has just a single quote in it, this script will fail with exception ORA-01756, which means there is no closing quote. How can I avoid this exception?
Just use double quotes to wrap the columns' names even in your script; for example, this works:
alter table test rename column "'test1" to test1
Your script could be edited as:
EXECUTE IMMEDIATE
'ALTER TABLE '
|| table_rec.TABLE_NAME
|| ' RENAME COLUMN "' /* open the quote */
|| column_rec.COLUMN_NAME
|| '" TO ' /* and close */
|| new_column_name;
Also, notice that in this way all the renamed columns will be upper case; if this is what you need, well done, otherwise you have to wrap with double quotes even the new names:
...
|| '" TO "' /* and close, and reopen */
|| new_column_name || '"'; /* and close again */

create or rename dynamic table teradata

I want to create or rename table with current_date.
For example:
1) create table TABLENAME || CURRENT_DATE
2) rename TABLE_NAME TO TABLE_NAME_||CURRENT_DATE.
How can I do it? Could you give an example?
This will append the current date in YYYYMMDD format to the table name. If YYYYMMDD is already appended to the name it's replaced with the new date.
REPLACE PROCEDURE rename_table_yyyymmdd
(
IN db_name VARCHAR(128) CHARACTER SET Unicode,
IN tbl_name VARCHAR(128) CHARACTER SET Unicode, -- defaults to current database
OUT msg VARCHAR(600) CHARACTER SET Unicode
) SQL SECURITY INVOKER
BEGIN
DECLARE old_name VARCHAR(261) CHARACTER SET Unicode;
DECLARE new_name VARCHAR(261) CHARACTER SET Unicode;
DECLARE sql_stmt VARCHAR(600) CHARACTER SET Unicode;
SET old_name = '"' || Coalesce(db_name,DATABASE) || '"."'
|| Coalesce(tbl_name, '') || '"';
SET new_name = '"' || Coalesce(db_name,DATABASE) || '"."'
-- remove an existing "_YYYYMMDD" at the end of the table name
|| Coalesce(RegExp_Replace(tbl_name, '_[0-9]{8}$'),'')
|| '_' || To_Char(Current_Date, 'yyyymmdd') || '"';
SET sql_stmt = 'RENAME TABLE ' || old_name || ' AS ' || new_name || ';';
EXECUTE IMMEDIATE sql_stmt;
SET msg = 'Table ' || old_name || ' renamed to ' || new_name;
END;
CALL rename_table_yyyymmdd('myDB', 'tablename', msg);
CALL rename_table_yyyymmdd(null, 'tablename', msg);
No error handling, simply fails on errors, e.g. when you run it twice a day or the table doesn't exists or the user has no Drop Table right, etc.
Step 1:
create table dat
(
saledate CURRENT_DATE
)
Step 2:
CREATE TABLE database.new_table AS
database.dat WITH DATA;
You can use sysdate or getdate to get current date.

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.

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.

Resources