Dynamic PL-SQL programming - Issue with DBMS_SQL.PARSE - sql

I wrote a query to fetch the details using some business logic. However, I got stuck at the below line
DBMS_SQL.PARSE(CUR, SQLSTR, DBMS_SQL.NATIVE);
And I got the below error. I checked the syntax again and again and it is perfectly fine. So, could you guys help me in fixing this? Errors below.
ORA-00933: SQL command not properly ended
ORA-06512: at "SYS.DBMS_SQL", line 1134
ORA-06512: at line 35
0093.00000 - "SQL command not properly ended"
Code is below.
DECLARE
SQLSTR VARCHAR2(30000);
CUR INTEGER;
SER INTEGER;
WS VARCHAR2(10);
REFCUR SYS_REFCURSOR;
TYPE ERMAS IS TABLE OF DUAL%ROWTYPE;
SERULT ERMAS;
VAL MRUL.OWL%TYPE;
CURSOR TION IS
SELECT RUD.COL, RUD.VAL, RUD.OPR, RUD.RU3
FROM RMAP RUM, RDET RUD
WHERE RUM.RU3 = RUD.RU3
AND RUD.RU3 IN ('60', '61', '62');
BEGIN
CUR := DBMS_SQL.OPEN_CURSOR;
SQLSTR := 'SELECT * '||CHR(13);
SQLSTR := SQLSTR || 'FROM DUAL '||CHR(13);
SQLSTR := SQLSTR || 'WHERE MRUL = : OWL AND (';
FOR DONC IN TION
LOOP
SQLSTR := SQLSTR || DONC.COL || DONC.OPR||':'|| DONC. RU3 ||'OR ';
END LOOP;
SQLSTR := REGEXP_REPLACE(SQLSTR, 'OR ', ')');
DBMS_SQL.PARSE(CUR, SQLSTR, DBMS_SQL.NATIVE);
SELECT OWL
INTO VAL
FROM MRUL
WHERE RU2 = '20';
DBMS_SQL.BIND_VARIABLE(CUR, ':OWL', WS);
FOR DONC IN TION
LOOP
DBMS_SQL.BIND_VARIABLE(CUR, ':'|| DONC.RU3, DONC. VAL);
END LOOP;
SER := DBMS_SQL.EXECUTE(CUR);
REFCUR := DBMS_SQL.TO_REFCURSOR(CUR);
FETCH REFCUR BULK COLLECT INTO SERULT;
END;
Updating the create and insert queries.
CREATE TABLE RDET
(
COL VARCHAR2(50) NOT NULL,
VAL VARCHAR2(50) NOT NULL,
OPR VARCHAR2(50) NOT NULL,
RU3 VARCHAR2(50) NOT NULL
);
CREATE TABLE RMAP
(
RU2 VARCHAR2(50) NOT NULL,
RU3 VARCHAR2(50) NOT NULL
);
CREATE TABLE MRUL
(
OWL VARCHAR2(50) NOT NULL,
RU2 VARCHAR2(50) NOT NULL
);
INSERT INTO RDET
VALUES (‘DELHI’, ‘CITY’, ‘=’, ‘60’);
INSERT INTO RDET
VALUES (‘SHIMLA’, ‘VILLAGE’, ‘<>’, ‘61’);
INSERT INTO RDET
VALUES (‘NOIDA’, ‘TOWN’, ‘=’, ‘62’);
INSERT INTO RMAP
VALUES (‘20’, ‘60’);
INSERT INTO RMAP
VALUES (‘21’, ‘61’);
INSERT INTO RMAP
VALUES (‘21’, ‘62’);
INSERT INTO MRUL
VALUES (‘COUNTRY’, ‘20’);
INSERT INTO MRUL
VALUES (‘CONTINENT, ‘21’);
INSERT INTO MRUL
VALUES (‘AREA’, ‘22’);
INSERT INTO MRUL
VALUES (‘AREA’, ‘23’);

The issue was that the SQL statement was incorrectly suffixed with 'OR'. So, did an RTRIM to remove it and the procedure worked.

Related

oracle sql : "get or insert" stored procedure

I would like to do a stored procedure that receive an input param and then
if targeted table does not contains that value, a new row is created and then the id of the created row is returned
if targeted table already contain input param, the id of the row is returned
For moment I only manage to insert new row only if input param is new:
--exemple of table with a primary id a column with value
create table unique_number_table (
id NUMBER(12) not null,
UNIQUE_NUMBER VARCHAR2(80) not null,
constraint PK_ID primary key (ID)
);
create sequence SEQ_NUMBER
INCREMENT BY 1
START WITH 2
MAXVALUE 999999999
MINVALUE 0;
create or replace procedure insert_or_get_unique_number ( input_number in varchar ) is
begin
insert into unique_number_table (id, UNIQUE_NUMBER)
select SEQ_NUMBER.NEXTVAL ,input_number
from dual
where not exists(select * from unique_number_table
where UNIQUE_NUMBER =input_number);
end insert_or_get_unique_number;
Do you know how to do this?
Seems to me like you want a stored function and not a procedure.
create or replace function insert_or_get_unique_number (input_number varchar2)
return UNIQUE_NUMBER_TABLE.ID%type
is
L_NUM UNIQUE_NUMBER_TABLE.ID%type;
begin
select ID
into L_NUM
from UNIQUE_NUMBER_TABLE
where UNIQUE_NUMBER = input_number;
return L_NUM;
exception
when NO_DATA_FOUND then
insert into unique_number_table (id, UNIQUE_NUMBER)
values (SEQ_NUMBER.NEXTVAL, input_number)
returning ID into L_NUM;
return L_NUM;
end insert_or_get_unique_number;
This is a possible solution to your problem.
CREATE OR REPLACE PROCEDURE insert_or_get_unique_number (
input_number IN VARCHAR,
c_out out sys_refcursor
) IS
Lv_input_exists INT;
lv_myRowid VARCHAR2(200);
err_code varchar2(600);
err_msg varchar2(500);
BEGIN
--step 1 check if the input param exists. -
select count(*)
INTO Lv_input_exists
FROM unique_number_table
WHERE unique_number = input_number;
--step 2 if it exists than get the rowid of that row and return that value
IF Lv_input_exists > 0
THEN
OPEN c_out for
SELECT ROWID
FROM unique_number_table Uni
WHERE uni.id = input_number ;
RETURN;
ELSE
-- STEP 3 the input number does not exists therefore we need to insert and return the rowid--
INSERT INTO unique_number_table (
id,
unique_number
)
VALUES(
seq_number.NEXTVAL,
input_number)
returning ROWID into lv_myRowid;
----STEP 4 Open the cursor and return get the rowid.
OPEN c_out for
SELECT lv_myRowid
FROM DUAL ;
SYS.dbms_output.put_line( 'Done' );
END IF;
EXCEPTION WHEN OTHERS THEN
err_code := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 200);
SYS.dbms_output.put_line( err_code || ' '||': '||err_msg );
END insert_or_get_unique_number;
you can test the procedure like so.
set serveroutput on ;
DECLARE
INPUT_NUMBER VARCHAR2(200);
C_OUT sys_refcursor;
BEGIN
INPUT_NUMBER := '3';
INSERT_OR_GET_UNIQUE_NUMBER(
INPUT_NUMBER => INPUT_NUMBER,
C_OUT => C_OUT
);
DBMS_SQL.return_result(C_OUT);
END;

Procedure to Insert into table and its columns by given parameters?

i`ll start this with some code...
CREATE OR REPLACE PROCEDURE LOAD_IMAGE_INTO_TABLE (
imgDir varchar2
, imgName varchar2
, destTable varchar2
, destIndex varchar2
, destBlob varchar2)
AS
fHnd bfile;
b blob;
srcOffset integer := 1;
dstOffset integer := 1;
BEGIN
dbms_lob.CreateTemporary( b, true );
fHnd := BFilename( imgDir, imgName );
dbms_lob.FileOpen( fHnd, DBMS_LOB.FILE_READONLY );
dbms_lob.LoadFromFile( b, fHnd, DBMS_LOB.LOBMAXSIZE, dstOffset, srcOffset );
--insert into (Select * From user_tables Where table_name = destTable) values(imgName,b);
--insert into destTable (destIndex, destBlob) values( imgName, b );
commit;
dbms_lob.FileClose( fHnd );
END LOAD_IMAGE_INTO_TABLE;
As you might see, I am trying to create a procedure which puts an image into a table. Target table name and column names (for imgname and blobfile) will be given as parameters.
Both ways I tried didn't work. (see --insert(...) in code)
Maybe you have any idea how to solve this?
Thanks in advance.

Is there any possibility of creating view or table based on the output of a dynamically opened ref cursor?

I have learnt how to return a dynamically opened ref cursor. Now based on the output, I want to create a table or a view.
Input table:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
Records
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
Query output
sales_name 10-MAR-2019 11-MAR-2019 12-MAR-2019
Norb 10000 5000 1000
Bert 5000 13000 4000
Alba 4000 0 2000
Rob 200000 80000 40000
Now the result should be saved in a table or a view. I have learnt how to return a dynamically opened ref cursor so far.
------Here is the procedure I used -----------
create or replace package p_sales_pkg
as
type rc is ref cursor;
procedure get_query( p_cursor in out rc, p_start date, p_end date );
end;
/
create or replace package body p_sales_pkg
as
procedure get_query( p_cursor in out rc, p_start date, p_end date )
is
l_query long := 'select s_name ';
begin
for i in 1 .. trunc(p_end)-trunc(p_start)+1
loop
l_query := l_query || ', sum( decode( trunc(s_date), ' ||
'to_date( ''' || to_char(p_start+i-1,'yyyymmdd') ||
''', ''yyyymmdd'' ), s_qty, 0 )) "' ||
to_char(p_start+i-1) || '"';
end loop;
l_query := l_query || ' from sales group by s_name';
open p_cursor for l_query;
end;
end;
/
set autoprint on
var x refcursor
exec nw_demo_pkg.get_query( :x, '10-MAR-19', '13-MAR-19' );
This is really a very nice and challenging question. I disagree on the #APC point on SELECT part of a CREATE TABLE ... AS SELECT statement. Well we definitely can't do that. What i believe is to every problem in Oracle, there exists a solution.
You requirement can be achieved using a NESTED TABLE. See below:
Set Up:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
/
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
---Created an Object of Sales table to hold intermediate result
create or replace type sales_obj is OBJECT
(obj_sale_name varchar2(20),
obj_date_sal date,
obj_qty number(10)
);
/
-- Table of Sales Object.
create or replace type vtest1Tab is table of sales_obj;
/
Anonymous Block to Create table ccc:
DECLARE
VAR VTEST1TAB ;
vsql varchar2(500);
BEGIN
vsql := 'create table ccc(col1) NESTED TABLE COL1 STORE AS TAB1
as
Select cast(multiset(Select * from SALES) as VTEST1TAB )
from dual
';
Execute immediate vsql ;
END;
Output:
SQL> Select p.*
from ccc c,
table(c.COL1) p ;
In this link , The reply by "Zlatko Sirotic" covers exactly how to identify columns of the cursor and print them.
Look for "dyn_fetch", as the package is generic enough, it can work with any query for printing data. You can use the same approach to insert the data into a table that is created dynamically.

Update Multiple Rows in same table after Update using Trigger

If i update ACK or ReJ column it should update all the other columns with the same GlobalID.
create table t_emp(
empid varchar2(10) not null,
empname varchar2(50),
Dep varchar2(50),
ACk number(5),
REJ number(5),
globalID varchar2(10) default '0'
);
insert into t_emp t values ( 'TM01' , 'Logu','Java',null,null,'01');
insert into t_emp t values ( 'BT01' , 'Logu','Java' ,null,null,'01');
insert into t_emp t values ( 'Oracle01' , 'Logu','DBA' ,null,null,'01');
insert into t_emp t values ( 'Google01' , 'Logu','Design' ,null,null,'0');
insert into t_emp t values ( 'AR02' , 'Uthaya','CRM' ,null,null,'02');
insert into t_emp t values ( 'RIL02' , 'Uthaya','Java' ,null,null,'02');
insert into t_emp t values ( 'EA02' , 'Uthaya','DBA' ,null,null,'02');
insert into t_emp t values ( 'TCS02' , 'Uthaya','Java' ,null,null,null);
insert into t_emp t values ( 'P05' , 'Krish','.Net' ,null,null,'05');
insert into t_emp t values ( 'TCS06' , 'Krish','.Net' ,null,null,'06');
insert into t_emp t values ( 'IBM06' , 'Krish','.Net' ,null,null,'06');
CREATE OR REPLACE TRIGGER t_emp_update
AFTER UPDATE
ON t_emp
FOR EACH ROW
DECLARE
t_Ack varchar2(15);
t_Rej varchar2(15);
t_globalID varchar2(10);
t_empid varchar2(10);
BEGIN
t_globalID := :new.globalID;
t_Ack := :new.ACk;
t_Rej := :new.REJ;
t_empid := :new.empid;
IF t_Ack is not null then
DBMS_OUTPUT.PUT_LINE('t_Ack := ' || t_Ack || ', t_globalID := '|| t_globalID ||', t_empid := '||t_empid);
update t_emp set ACk = t_Ack where globalID = t_globalID and empid != t_empid;
end if;
IF t_Rej is not null then
DBMS_OUTPUT.PUT_LINE('t_REJ := ' || t_Rej || ', t_globalID := '|| t_globalID ||', t_empid := '||t_empid);
update t_emp set Rej = t_Rej where globalID = t_globalID and empid != t_empid;
end if;
END;
update t_emp v set Rej = 1 where empid = 'TCS06';
If I Update empid = 'TCS06' it should Update Internally all rows with same globalID (06).
select * from t_emp order by empname,globalID;
I am getting some errors in this trigger.
ORA-04091: table TEST1.T_EMP is mutating, trigger/function may not see it
ORA-06512: at "TEST1.T_EMP_UPDATE", line 17
ORA-04088: error during execution of trigger 'TEST1.T_EMP_UPDATE'
Kindly Help me ...
This link and this other one should be helpfull to understand the error and how to correct it. Also check sample of an AFTER UPDATE trigger.
Check a similar question here at SO with a detailed answer.
Please refer these links:
INSERT trigger for inserting record in same table(Stackoverflow)
Avoiding Mutating Tables

Dynamic PL/SQL query, how to ignore null parameters?

I have a PL/SQL procedure with multiple parameters. When a webapp calls the procedure, if it's not using a certain parameter, it passes is as null, ie
procedure test (param1 in varchar2, param2 in varchar2, param3 in varchar2, cursor out sys_refcursor)
...
end procedure test;
I want to make an SQL query where I include the given parameters in the WHERE clause only if the parameter is not null. Is there a way to achieve this in an elegant way, other than building the SQL query in a string and then opening the cursor for that string, like this
vSQL := 'SELECT * from TABLE WHERE something = something_else';
if param1 IS NOT NULL
vSQL := vSQL || 'AND some_param = ' || param1;
I have answered a similar question on dba.stackexchange.com
You can code it all into a single select
SELECT * from TABLE WHERE something = something_else
AND ((param1 IS NOT NULL AND some_param = param1) OR 1)
Create a test table:
create table testtab
(
name_first varchar2(50),
name_last varchar2(50),
name_middle varchar2(50)
);
insert into testtab values ('Joe', 'Jones', 'A');
insert into testtab values ('Joe', 'Smith', 'A');
insert into testtab values ('Steve', 'Jones', 'B');
insert into testtab values ('Axl', 'Rose', 'C');
insert into testtab values ('Phil', 'McCracken', 'D');
commit;
Create your procedure:
CREATE OR REPLACE procedure ECDATA.get_testtab_rows
(i_name_first in varchar2 default null,
i_name_last in varchar2 default null,
i_name_middle in varchar2 default null,
o_cursor out sys_refcursor
) as
v_result_cur sys_refcursor;
begin
open v_result_cur for
select * from testtab
where name_first like nvl(i_name_first, '%')
and name_last like nvl(i_name_last, '%')
and name_middle like nvl(i_name_middle, '%')
;
o_cursor := v_result_cur;
end;
/
Then call it like this:
declare
v_cur sys_refcursor;
testtab_rec testtab%rowtype;
begin
get_testtab_rows(i_name_last=>'Jones', o_cursor=>v_cur);
loop
fetch v_cur into testtab_rec;
exit when v_cur%notfound;
dbms_output.put_line(testtab_rec.name_first || ' ' || testtab_rec.name_middle || ' ' || testtab_rec.name_last);
end loop;
exception
when others then raise;
end;