oracle sql : "get or insert" stored procedure - sql

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;

Related

How to avoid mutating table error while using a trigger?

I need to insert rows in a Table_A, with values from Table_B on UPDATE of Table_B.
table_A (
station int,
state varchar(20),
CONSTRAINT pk_table_a PRIMARY KEY(station, state),
CONSTRAINT fk_table_A FOREIGN KEY (station, state)
REFERENCES table_B (station, state)
)
table_B (
station int,
state varchar(20),
player int,
date_sent Date DEFAULT NULL
CONSTRAINT pk_table_b PRIMARY KEY(station, state, player)
)
Now, my triggers needs to add a row to table_A (station, state), when all the dates for these (station, state) become NOT NULL in table_B.
Here's my actual which causes mutating table error:
CREATE OR REPLACE TRIGGER add_stations_sent
AFTER INSERT OR UPDATE ON "TABLE_B"
FOR EACH ROW
WHEN (NEW.DATE_SENT IS NOT NULL)
DECLARE
nb_stations_null number;
BEGIN
SELECT COUNT(1)
INTO nbr_stations_null
FROM "TABLE_B"
WHERE "TABLE_B".STATE = :NEW.STATE AND
"TABLE_B".STATION <> :NEW.STATION AND
"TABLE_B".DATE_SENT IS NULL;
IF (nb_stations_null = 0) THEN
INSERT INTO "TABLE_A" VALUES (:NEW.STATION, :NEW.STATE);
END IF;
END;
Something like this will defer the processing to AFTER STATEMENT level and thus allow to you run queries
create or replace
trigger avoid_mutate
for insert or update on table_b
compound trigger
l_station sys.odcivarchar2list := sys.odcivarchar2list();
l_state sys.odcivarchar2list := sys.odcivarchar2list();
before each row is
begin
l_station.extend;
l_state.extend;
l_station(l_station.count) := :new.station;
l_state(l_state.count) := :new.state;
end before each row;
after statement is
declare
nb_stations_null number;
begin
for i in 1 .. l_station.count loop
SELECT COUNT(1)
INTO nbr_stations_null
FROM "TABLE_B"
WHERE "TABLE_B".STATE = l_state(i) AND
"TABLE_B".STATION <> l_station(i) AND
"TABLE_B".DATE_SENT IS NULL;
IF (nb_stations_null = 0) THEN
INSERT INTO "TABLE_A" VALUES (l_station(i), l_state(i));
END IF;
end after statement;
end;
/

Copy data for another table using Collections Types index-by table (associated with a table)

I don't know how to create a function with collections type. I am familiar with SQL, but I do not know that particular function. Here is what I tried:
Create or Replace Function COPY_EMPLOYEES_WITH_RT(
Begin
insert into jjj_employees ( select * from employees)
I want to create a function COPY EMPLOYEES_WITH_RT and copy data from the table EMPLOYEES to jjj_EMPLOYEES using Collections Types index-by table (associated with a table).
CREATE OR REPLACE FUNCTION COPY_EMPLOYEES_WITH_RT(O_ERROR_MESSAGE OUT VARCHAR)
--DECLARE type
TYPE EMP_RECORD IS TABLE OF employees%ROWTYPE;
l_emp EMP_RECORD;
-- define cursor
CURSOR c_employees IS
SELECT *
FROM employees;
--
BEGIN
--
open c_employees;
loop
fetch c_employees
bulk collect into l_emp limit 1000;
exit when l_emp.count = 0;
-- Process contents of collection here.
Insert into jrf_employees values l_emp;
END LOOP;
CLOSE c_employees;
EXCEPTION
--
WHEN OTHERS THEN
O_error_message := SQLERRM;
END;
/
it's like something like that, i just didn't know, what is wrong
If you are using object-relational tables and particularly want to use PL/SQL associative arrays (index-by table types) then you can create your tables as:
CREATE TYPE employee_t IS OBJECT(
id NUMBER(10,0),
first_name VARCHAR2(20),
last_name VARCHAR2(20)
);
CREATE TABLE employees OF employee_t (
id PRIMARY KEY
);
CREATE TABLE jjj_employees OF employee_t (
id PRIMARY KEY
);
With the sample data:
INSERT INTO employees
SELECT 1, 'One', 'Uno' FROM DUAL UNION ALL
SELECT 2, 'Two', 'Dos' FROM DUAL UNION ALL
SELECT 3, 'Three', 'Tres' FROM DUAL;
And the function as:
CREATE FUNCTION COPY_EMPLOYEES_WITH_RT
RETURN NUMBER
IS
TYPE employee_a IS TABLE OF employee_t INDEX BY PLS_INTEGER;
emps employee_a;
i PLS_INTEGER;
BEGIN
FOR r IN ( SELECT VALUE(e) AS employee FROM employees e )
LOOP
emps( r.employee.id ) := r.employee;
END LOOP;
i := emps.FIRST;
WHILE i IS NOT NULL LOOP
INSERT INTO jjj_employees VALUES ( emps(i) );
i := emps.NEXT(i);
END LOOP;
RETURN 1;
END;
/
Then you can run the function using:
BEGIN
DBMS_OUTPUT.PUT_LINE( COPY_EMPLOYEES_WITH_RT() );
END;
/
And the table is copied as:
SELECT * FROM jjj_employees;
Outputs:
ID | FIRST_NAME | LAST_NAME
-: | :--------- | :--------
1 | One | Uno
2 | Two | Dos
3 | Three | Tres
db<>fiddle here
Update
Since you appear to want to use a collection and not an associative array:
CREATE OR REPLACE FUNCTION COPY_EMPLOYEES_WITH_RT
RETURN VARCHAR2
IS
--DECLARE type
TYPE EMP_RECORD IS TABLE OF employees%ROWTYPE;
l_emp EMP_RECORD;
-- DECLARE cursor
CURSOR c_employees IS
SELECT *
FROM employees;
BEGIN
OPEN c_employees;
LOOP
FETCH c_employees
BULK COLLECT INTO l_emp LIMIT 1000;
EXIT WHEN l_emp.COUNT = 0;
-- Process contents of collection here.
FORALL i IN 1 .. l_emp.COUNT
INSERT INTO jjj_employees VALUES l_emp(i);
END LOOP;
CLOSE c_employees;
RETURN NULL;
EXCEPTION
WHEN OTHERS THEN
RETURN SQLERRM;
END;
/
db<>fiddle

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.

Update column value after some validation on it

I have a scenario where i need to update the value of a column or say append !
Below is test table and data
create table test (rsrc_nm varchar2(50), parm varchar2(400));
insert into test values ('HLRCamelProfileBasic','resource_code:7|resource_type:HLR_Camel_Profile|Priority:1|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:1|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:2|');
insert into test values ('HSSUSERProfileBasic','resource_code:3|resource_type:HSS_User_Profile|Priority:1|');
insert into test values ('HLRBaseProfileBasic','resource_code:1|resource_type:HLR_Base_Profile|Priority:2|');
insert into test values ('HLRBaseProfileBasic','resource_code:1|resource_type:HLR_Base_Profile|Priority:3|');
Here we have 2 columns of a staging table in which rsrc_nm and parm are given what i have to do is i need to update or append the rsrc_nm column for parm coulmn where the last value i.e. priority is changing from 1 to 2 for same rsrc_nm.
For example
rsrc_ nm -- HSSUSERProfileBasic parm -- resource_code:3|resource_type:HSS_User_Profile|Priority:1|
rsrc_ nm -- HSSUSERProfileBasic parm -- resource_code:3|resource_type:HSS_User_Profile|Priority:2|
Here we have same parm priority but the values are different and i need to insert these values into another table for which i need to separate all PIPE values so while i insert it in the table it gives me error for unique constraint because that table table have 3 columns
RSRC_NM PARM VAL
------------------------------- -------------- ----------------
HSSUSERProfileBasic Priority 1
HLRCamelProfileBasic Priority 1
HLRBaseProfileBasic Priority 2
And on this table we have primary key on first two columns which enforce unique constraint also so i can not insert " HLRCamelProfileBasic " rsrc_nm for parm " Priority " because it fails in uniqueness.
So i find a solution to overcome this if i can append or update the rsrc_nm HLRCamelProfileBasic to " HLRCamelProfileBasic_1 " for " Priority:1 " and HLRCamelProfileBasic_2 for " Priority:2 " and so on for all RSRC_NM in Staging table.
My solution --
declare
cursor c1 is select * from RSRC_SVC_MAPPING where parm_entry like '%Priorit%';
parm_entry_length number;
l_value varchar2(100);
parm_name varchar2(100);
parm_val varchar2(100);
l_c1 c1%rowtype;
l_cnt number := 0;
l_rsrc_nm varchar2(100);
begin
open c1;
loop
fetch c1 into l_c1;
parm_entry_length := length(l_c1.Parm_Entry) - length(replace(l_c1.Parm_Entry,'|',''));
for i in 1 .. parm_entry_length loop
select regexp_substr(l_c1.Parm_Entry,'[^|]+',1,i) into l_value from dual;
select regexp_substr (l_value, '[^:]+', 1, 1) into parm_name from dual;
select regexp_substr (l_value, '[^:]+', 1, 2) into parm_val from dual;
-- dbms_output.put_line(l_value||' '||parm_name||' '||parm_val||' '||l_c1.resourcename);
for r in ( select count(*) cnt, resourcename
-- into l_cnt , l_rsrc_nm
from (select count(*), resourcename, parm_entry from RSRC_SVC_MAPPING where parm_entry like '%Priorit%' group by resourcename, parm_entry) group by resourcename)
loop
if r.cnt > 1
then
dbms_output.put_line(l_value||' '||parm_name||' '||parm_val||' '||l_c1.resourcename);
update RSRC_SVC_MAPPING
set resourcename = r.resourcename||'_'||l_cnt
where resourcename = r.resourcename;
l_cnt := l_cnt +1;
end if;
end loop;
end loop;
exit when c1%notfound;
end loop;
exception
when others then
dbms_output.put_line('ERROR OCCURED '||SQLCODE||' '||sqlerrm);
dbms_output.put_line(dbms_utility.format_error_backtrace());
end;
Data which needs to be like after UPDATE is like
HSSUSERProfileBasic_1 resource_code:3|resource_type:HSS_User_Profile|Priority:1|
HSSUSERProfileBasic_2 resource_code:3|resource_type:HSS_User_Profile|Priority:2|
HLRBaseProfileBasic_2 resource_code:1|resource_type:HLR_Base_Profile|Priority:2|
HLRBaseProfileBasic_3 resource_code:1|resource_type:HLR_Base_Profile|Priority:3|
Here we have changed the value rsrc_nm column and append it with the value of priority i.e. 1 and 2
target table is like
create table target (rsrc_nm varchar2(100), parm varchar2(50), val varchar2(50) constraint tgt_pk primary key (rsrrc_nm, parm));
MY DB is -- Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production

How to Create Stored procedure in DB2

Can you please help me to create a below Oracle procedure in DB2? Same table name with columns are available in DB2 also but below script is not working
CREATE OR REPLACE PROCEDURE sample_proc (ACCT_NO in CHAR,p_cursor out SYS_REFCURSOR)
is
BEGIN
OPEN p_cursor FOR
select sampl1,sample2,sample3
from
table_test b
where
rec_id='A'
and sample3=ACCT_NO ;
END;
If you want a return better use function here are some example to get collection
CREATE TABLE table_test
(
sample1 VARCHAR2 (1000),
sample2 VARCHAR2 (1000),
sample3 VARCHAR2 (1000)
);
insert into table_test ( sample1,sample2 ,sample3)
values ('daftest1','dsdtest1','sstsest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('FAStest1','fstest1','sstsest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('sdtest1','asdtest1','fstest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('test2','test2','test123');
CREATE OR REPLACE TYPE TEST_REC
AS OBJECT (
sample1 VARCHAR2(1000)
,sample2 VARCHAR2(1000)
,sample3 VARCHAR2(1000)
);
CREATE OR REPLACE TYPE TEST_REPORT_TABLE
AS TABLE OF TEST_REC;
CREATE OR REPLACE FUNCTION testing (p_acct_no IN varchar2)
RETURN test_report_table
IS
v_rec test_rec;
v_test_report_table test_report_table := test_report_table();
BEGIN
FOR i IN (SELECT sample1,sample2,sample3
FROM table_test b
--where rec_id='A'
where sample3=p_acct_no)
LOOP
v_rec:=test_rec(NULL,NULL,NULL);
dbms_output.put_line(i.sample1);
v_rec.sample1:=i.sample1;
v_rec.sample2:=i.sample2;
v_rec.sample3:=i.sample3;
v_test_report_table.EXTEND;
v_test_report_table(v_test_report_table.COUNT) :=v_rec;
END LOOP;
RETURN v_test_report_table;
END;
select * from table(testing(p_acct_no=>'sstsest3'))
Or better use bulk collect if you don't need to add rows dynamically
CREATE OR REPLACE FUNCTION JSTRAUTI.testing (p_acct_no IN varchar2)
RETURN test_report_table
IS
v_test_report_table test_report_table := test_report_table();
BEGIN
SELECT test_rec(sample1,sample2,sample3)
bulk collect into v_test_report_table
FROM table_test b
--where rec_id='A'
where sample3=p_acct_no;
RETURN v_test_report_table;
END;
result the same.