How to declare a Cursors With different conditions - oracle

I have a procedure EMPHIRESEPCHAN which is used to fetch the employees list who are hired, seperated and changed their titles based on a particular time frame. The procedure is as follows:
PROCEDURE EMPHIRESEPCHAN ( p_Start in VarChar2, p_End in VarChar2,
p_Hire IN VarChar2, p_Sep IN VarChar2, p_Changed IN VarChar2, p_Condition1 IN VarChar2, p_Condition2 IN VarChar2)
IS
CURSOR c_emplst ( p_listtype varchar2 ) IS
select e.emp_id, e.name, e.Rank
from person.emp e
where emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and dcode in
(select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) );
CURSOR c_promolst ( p_emp_id varchar2 ) IS
select pdate
from person.promo
where emp_id = p_emp_id
order by 2 desc;
Begin
for EmpRec in c_emplst ('HIRE')
LOOP
for PromoRec in c_PromoLst ( EmpRec.emp )
LOOP
if PromoRec.Dcode in ('TEMPORARY','RETURN','APPOINTED' )
-- Do all the operation
end if;
end loop;
end loop;
end EMPHIRESEPCHAN;
I have to modify the procedure to retrieve the employee list based on p_Condition1 and p_Condition2 parameters.
If the p_Condition1 is not null and p_Condition2 is null, I have to retrieve the employees who have Rank = 'Developer'
If the p_Condition1 is null and p_Condition2 is not null I have to retrieve the employees who have Rank = 'Tester'
If the p_Condition1 and p_Condition2 is not null I have to retrieve the employees who have Rank both 'Developer' and 'Tester'.
I read so many posts in various sites and found answers which I was not able to follow.
Based on the posts, I made modifications to the cursor as follows
CURSOR c_emplst ( p_listtype varchar2 ) IS
select e.emp_id, e.name, e.Rank
from person.emp e
where ( p_Condition1 = null and p_Condition2 = null = and emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and dcode in (select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) )
or ( p_Condition1 > null and p_Condition2 = null = and emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and Rank ='Developer'
and dcode in (select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) )
or ( p_Condition1 = null and p_Condition2 > null = and emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and Rank = 'Tester'
and dcode in (select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) );
However it's not working.
Thanks for your time and consideration.

I suspect these conditions are your problem:
p_Condition1 = null
Nothing is ever equal to NULL. NULL is not even equal to NULL. Instead, use:
p_Condition1 IS NULL

Related

HOW TO CALL THIS PROCEDURE?

Here I'm Creating procedure to inner join two tables.
create or replace PROCEDURE TBL_EMPLOYEE_PROJECT(
EMP_ID IN TBL_EMPLOYEE.EMPLOYEE_ID%TYPE,
CUR OUT SYS_REFCURSOR)
IS
BEGIN
OPEN CUR FOR
SELECT E.EMPLOYEE_ID EMPLOYEE_ID,
E.FIRST_NAME FIRST_NAME,
E.LAST_NAME LAST_NAME,
E.MIDDLE_NAME MIDDLE_NAME,
E.SALARY SALARY,
E.DATE_OF_BIRTH DATE_OF_BIRTH,
E.DEPARTMENT_ID DEPARTMENT_ID,
E.INSERTED_BY INSERTED_BY,
E.INSERTED_DT INSERTED_DT,
E.UPDATE_BY UPDATE_BY,
E.UPDATED_DT UPDATED_DT,
E.DELETED_BY DELETED_BY,
E.DELETED_DT DELETED_DT,
E.MANAGER_ID MANAGER_ID,
D.DEPARTMENT_ID DEPARTMENT_ID_0,
D.DEPARTMENT_NAME DEPARTMENT_NAME,
D.DEPARTMENT_LOCATION DEPARTMENT_LOCATION
FROM TBL_EMPLOYEE E
INNER JOIN DEPARTMENT D
ON ( E.DEPARTMENT_ID = D.DEPARTMENT_ID )
WHERE E.EMPLOYEE_ID = EMP_ID;
END;
But Don't Know How To Call This Stored Procedure.
Please Tell How To Call This Procedure
Declare
returned_refcursor SYS_REFCURSOR := Null;
Begin
BL_EMPLOYEE_PROJECT(some_emp_id, returned_refcursor);
-- variable returned_refcursor now has a pointer to the CUR from procedure
End;
Here is a simple example
create or replace PROCEDURE AA(
EMP_ID IN VARCHAR2,
CUR OUT SYS_REFCURSOR)
IS
BEGIN
OPEN CUR FOR
Select * From
(
SELECT '12' "ID", 'Name for 12' "NAME" FROM DUAL UNION ALL
SELECT '13' "ID", 'Name for 13' "NAME" FROM DUAL
)
WHERE ID = EMP_ID;
END;
-- Use it
Declare
returned_refcursor SYS_REFCURSOR := Null;
myId VarChar2(10);
myName VarChar2(20);
Begin
AA('12', returned_refcursor);
-- variable returned_refcursor now has a pointer to the CUR from procedure
Fetch returned_refcursor InTo myID, myName;
DBMS_OUTPUT.PUT_LINE(myId);
DBMS_OUTPUT.PUT_LINE(myName);
End
-- Result
-- anonymous block completed
-- 12
-- Name for 12

getting ORA-01422: exact fetch returns more than requested number of rows while calling a procedure

CREATE TABLE cmb_staging (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
insert into cmb_staging values(1,'A','AA',null,null);
insert into cmb_staging values(1,'B','BB',null,null);
insert into cmb_staging values(2,'C','CC',null,null);
insert into cmb_staging values(2,'D','DD',null,null);
insert into cmb_staging values(3,'A','AA',null,null);
CREATE TABLE cmb_target (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30)
);
CREATE TABLE cmb_reject (
e_id NUMBER(10),
e_name VARCHAR2(30),
e_loc VARCHAR2(30),
validation_status VARCHAR2(30),
validation_result VARCHAR2(30)
);
CREATE TABLE SUMMARY_TAB
( TOT_RECORDS NUMBER(10,0),
SUCCESS_RECORDS NUMBER(10,0),
FAILED_RECORDS NUMBER(10,0),
PROCESS_STATUS VARCHAR2(30)
);
Procedure :
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
lv_succ_rec := 0;
lv_fail_rec := 0;
UPDATE cmb_staging
SET validation_status = 'Fail',
validation_result = CASE
WHEN e_id IS NULL
THEN 'Id is not present'
ELSE 'Id is longer than expected'
END
WHERE e_id is null
OR LENGTH(e_id) > 4;
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
if lv_count < 1 then
MERGE INTO cmb_target t
USING (SELECT e_id,
e_name,
e_loc
FROM cmb_staging
WHERE validation_status IS NULL) S
ON (t.e_id = S.e_id)
WHEN MATCHED THEN UPDATE SET
t.e_name = s.e_name,
t.e_loc = s.e_loc
WHEN NOT MATCHED THEN INSERT (t.e_id,t.e_name,t.e_loc)
VALUES (s.e_id,s.e_name,s.e_loc);
lv_succ_rec := SQL%ROWCOUNT;
else
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
end if;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
COMMIT;
ov_err_msg := 'Procedure completed succesfully';
EXCEPTION
WHEN OTHERS THEN
ov_err_msg := 'Procedure end up with errors'|| sqlerrm;
ROLLBACK;
END sp_dup_rec;
Calling a procedure :
set serveroutput on;
declare
v_err_msg varchar2(100);
begin
sp_dup_rec(v_err_msg);
dbms_output.put_line(v_err_msg);
end;
Hi Team,
I am getting ora-01422 error while calling a procedure. Basically, I want to insert duplicate records into the cmb_reject tab because the merge statement will fail and I will get ora - 30296 error if I will use only merge. SO, I have written if condition wherein it will fetch the count and if the count is more then will insert into cmb_reject tab
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
This raises the exception as you are trying to insert all 5 e_id values into a single variable and it raises a TOO_MANY_ROWS exception. (Apart from the fact that it is not identifying duplicates.)
Rather than trying to identify duplicates as a separate part of the process, you can do all the processing in the original UPDATE (in this case, converting it to a MERGE statement):
create or replace procedure sp_dup_rec(ov_err_msg OUT varchar2)
is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count number(30);
begin
MERGE INTO cmb_staging dst
USING (
SELECT ROWID AS rid,
CASE
WHEN e_id IS NULL
THEN 'Id is not present'
WHEN LENGTH(e_id) > 4
THEN 'Id is longer than expected'
WHEN num_e_id > 1
THEN 'Duplicate IDs'
END AS failure_reason
FROM (
SELECT e_id,
COUNT(*) OVER (PARTITION BY e_id) AS num_e_id
FROM cmb_staging
)
WHERE e_id IS NULL
OR LENGTH(e_id) > 4
OR num_e_id > 1
) src
ON (src.rid = dst.ROWID)
WHEN MATCHED THEN
UPDATE
SET validation_status = 'Fail',
validation_result = failure_reason;
MERGE INTO cmb_target t
USING (
SELECT e_id,
e_name,
e_loc
FROM cmb_staging
WHERE validation_status IS NULL
) S
ON (t.e_id = S.e_id)
WHEN MATCHED THEN
UPDATE
SET t.e_name = s.e_name,
t.e_loc = s.e_loc
WHEN NOT MATCHED THEN
INSERT (t.e_id,t.e_name,t.e_loc)
VALUES (s.e_id,s.e_name,s.e_loc);
lv_succ_rec := SQL%ROWCOUNT;
insert into cmb_reject
select s.*
from cmb_staging s
WHERE validation_status = 'Fail';
lv_fail_rec := SQL%ROWCOUNT;
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(
tot_records,
success_records,
failed_records
) values (
lv_succ_rec + lv_fail_rec,
lv_succ_rec,
lv_fail_rec
);
COMMIT;
ov_err_msg := 'Procedure completed succesfully';
EXCEPTION
WHEN OTHERS THEN
ov_err_msg := 'Procedure end up with errors'|| sqlerrm;
ROLLBACK;
END sp_dup_rec;
/
db<>fiddle here
The error stack should tell you the line that is throwing the error. My guess is that the line is
select e_id
into lv_count
from cmb_staging;
because that query doesn't make sense. If there are multiple rows in cmb_staging, the query will throw the ORA-01422 error because you can't put multiple rows in a scalar variable. It seems highly probable that you'd at least want your query to do a count. And most likely with some sort of predicate. Something like
select count(*)
into lv_count
from cmb_staging;
would avoid the error. But that likely doesn't make sense given your comments where you say "it should go into the cmb_reject table" which implies that there is a singular object. This change would cause lv_count > 0 whenever there were any rows in cmb_staging and it seems unlikely that you want to always reject rows.
My rough guess is that you really mean "two rows with the same e_id" when you say "duplicate". If that's right
select e_id, count(*)
from cmb_staging
group by e_id
having count(*) > 1
will show you the duplicate e_id values. You could then
insert into cmb_reject
select s.*
from cmb_staging s
where e_id in (select s2.e_id
from cmb_staging s2
group by s2.e_id
having count(*) > 1);
If you don't really care about performance (I'm assuming based on the questions you've asked that you're just learning PL/SQL and you're not trying to manage a multi-TB data warehouse load) and just want to process the records in a loop
for stg in (select s.*,
count(*) over (partition by s.e_id) cnt
from cmb_staging s)
loop
if( stg.cnt > 1 )
then
insert into cmb_reject( e_id, e_name, e_loc )
values( stg.e_id, stg.e_name, stg.e_loc );
l_fail_cnt := l_fail_cnt + 1;
else
merge into cmb_target tgt
using( select stg.e_id e_id,
stg.e_name e_name,
stg.e_loc e_loc
from dual ) src
on( src.e_id = tgt.e_id )
when not matched
then
insert( e_id, e_name, e_loc )
values( src.e_id, src.e_name, src.e_loc )
when matched
then
update set e_name = src.e_name,
e_loc = src.e_loc;
l_success_cnt := l_success_cnt + 1;
end if;
end loop;
This is a problem:
--If there are duplicates id then it should go into cmb_reject table
select e_id into lv_count from cmb_staging;
As there's no WHERE clause which would restrict number of rows being returned, if cmb_staging contains 2 (or more) rows, query will fail because you can't put that many rows into a scalar lv_count variable.
Looks like you actually meant to say
select count(*) into lv_count from cmb_staging;
as the next line says
if lv_count < 1 then

inserting parameters from select into procedure

I've got this select :
SELECT gp.name, gda.value
FROM game_definition_aff gda,
GAME_PARAMETAR gp,
game_aff ga,
game_name gn
WHERE 4355 = ga.aff_id
AND ga.id = gda.game_aff_id
AND gp.id = gda.game_parametar_id
AND 15 = ga.game_name_id
AND gn.game_name_id = ga.game_name_id
and gp.name in ( 'MIN_BET','MAX_BET','MAX_WIN')
which returns :
NAME - VALUE
MAX_WIN - 100
MAX_BET - 50
MIN_BET - 10
And i've got procedure :
get_percentage (i_player_id, o_session_id, royal_tri_win, o_percentage,
o_min_bet,
o_max_bet,
o_max_win,
o_pot
);
Now I need to insert values from select max_win,max_bet and min_bet into procedures parameters o_max_win,o_max_bet and o_bet_bet..
How can I do that ?
PS. this is procedure where is this code from above. and where are defined all parameters that is used in it..
PROCEDURE open_session_3w (
i_player_id NUMBER,
old_session_id NUMBER,
i_ip_address VARCHAR2,
i_machine_number VARCHAR2,
o_last_bet OUT NUMBER,
o_min_bet OUT NUMBER,
o_max_bet OUT NUMBER,
o_max_win OUT NUMBER,
o_credits OUT NUMBER,
o_session_id OUT NUMBER,
o_state OUT VARCHAR2
)
IS
o_percentage NUMBER;
o_pot NUMBER;
pom weak_cur;
p_active_session NUMBER;
p_parent number;
v_max_win number;
v_min_bet number;
v_max_bet number;
BEGIN
select parent_id into p_parent from casino_users where party_id = i_player_id;
check_pl_sess_3w (i_player_id, old_session_id);
o_session_id :=
player.open_new_session (i_player_id, i_ip_address,i_machine_number,'GAME SESSION');
select MAX( CASE WHEN gp.name = 'MAX_WIN' THEN VALUE END ),
MAX( CASE WHEN gp.name = 'MAX_BET' THEN VALUE END ),
MAX( CASE WHEN gp.name = 'MIN_BET' THEN VALUE END )
INTO v_max_win,v_max_bet,v_min_bet
FROM game_definition_aff gda,
GAME_PARAMETAR gp,
game_aff ga,
game_name gn
WHERE i_player_id = ga.aff_id
AND ga.id = gda.game_aff_id
AND gp.id = gda.game_parametar_id
AND 15 = ga.game_name_id
AND gn.game_name_id = ga.game_name_id
and gp.name in ( 'MIN_BET','MAX_BET','MAX_WIN') ;
player.get_percentage (i_player_id,
o_session_id,
royal_tri_win,
o_percentage,
COALESCE(v_min_bet,o_min_bet),
COALESCE(v_max_bet,o_max_bet),
COALESCE(v_max_win,o_max_win),
o_pot
);
Use MAX(CASE WHEN) and store each value in 3 variables.
DECLARE
v_max_win NUMBER;
v_max_bet NUMBER;
v_min_bet NUMBER;
BEGIN
select MAX( CASE WHEN gp.name = 'MAX_WIN' THEN VALUE END ),
MAX( CASE WHEN gp.name = 'MAX_BET' THEN VALUE END ),
MAX( CASE WHEN gp.name = 'MIN_BET' THEN VALUE END )
INTO v_max_win,v_max_bet,v_min_bet
FROM
game_definition_aff gda,
GAME_PARAMETAR gp,
game_aff ga,
game_name gn
..
..
get_percentage (i_player_id, o_session_id, royal_tri_win, o_percentage,
COALESCE( v_min_bet,o_min_bet),
COALESCE( v_max_bet,o_min_bet),
COALESCE( v_min_win,o_max_win),
o_pot
);
END;
/
Alternatively if you want to avoid local variables, you may use implicit cursor loop.
for rec in ( --select query above )
LOOP
get_percentage (i_player_id, o_session_id, royal_tri_win, o_percentage,
COALESCE( rec.min_bet,o_min_bet),
COALESCE( rec.max_bet,o_min_bet),
COALESCE( rec.min_win,o_max_win),
pot
);
END LOOP;

Updating column is not working in Oracle PLSQL

I have a Stored procedure in PLSQL which Inserts and Updates records on the basis of some condition.
Now here the issue is. While Inserting the record for the first time, it inserts records properly as required but
while updating it doesn't updates the record of the table.
Below is SP
PROCEDURE INSERT_INTO_VSAT_MST_DATA
(
P_SAPID IN NVARCHAR2,
P_CIRCLE IN NVARCHAR2,
P_CANDIDATEID IN NVARCHAR2,
P_SITEID IN NVARCHAR2,
P_PRIORITYID IN NVARCHAR2,
P_SITENAME IN NVARCHAR2,
P_LATITUDE IN NVARCHAR2,
P_LONGITUDE IN NVARCHAR2,
P_CONTACT_DETAILS IN CLOB,
P_SITETYPE IN NVARCHAR2,
P_SITE_PLOT_DIMENSION IN NUMBER,
P_TECHNOLOGY IN NVARCHAR2
)
AS
V_COUNT NUMBER:=0;
V_PANAROMICIMG_COUNT NUMBER:=0;
V_SATELLITEIMG_COUNT NUMBER:=0;
V_SITEPLOTIMG_COUNT NUMBER:=0;
V_VSAT_DETAIL_ID NUMBER:=0;
BEGIN
SELECT COUNT(VSAT_DETAIL_ID) INTO V_COUNT FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
IF V_COUNT > 0 THEN
SELECT VSAT_DETAIL_ID INTO TBL_INSERT FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
UPDATE TBL_VSAT_MST_DETAIL SET
CIRCLE = P_CIRCLE,
CONTACT_DETAILS = P_CONTACT_DETAILS,
SITE_TYPE = P_SITETYPE,
SITE_DETAILS_DIMENSION = P_SITE_PLOT_DIMENSION,
SITE_DETAILS_TECHNOLOGY = P_TECHNOLOGY
WHERE VSAT_DETAIL_ID = V_VSAT_DETAIL_ID
RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
ELSE
INSERT INTO TBL_VSAT_MST_DETAIL
(
SAP_ID,
CIRCLE,
CANDIDATE_ID,
SITE_ID,
PRIORITY,
SITE_NAME,
LATITUDE,
LONGITUDE,
CONTACT_DETAILS,
SITE_TYPE,
SITE_DETAILS_DIMENSION,
SITE_DETAILS_TECHNOLOGY
VALUES
(
P_SAPID,
P_CIRCLE,
P_CANDIDATEID,
P_SITEID,
P_PRIORITYID,
P_SITENAME,
P_LATITUDE,
P_LONGITUDE,
P_CONTACT_DETAILS,
P_SITETYPE,
P_SITE_PLOT_DIMENSION,
P_TECHNOLOGY
) RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
END IF;
IF TBL_INSERT > 0 THEN
BEGIN
SELECT COUNT(*) INTO V_PANAROMICIMG_COUNT FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Panaromic' AND IMG_ID = TBL_INSERT;
SELECT COUNT(*) INTO V_SATELLITEIMG_COUNT FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Satellite' AND IMG_ID = TBL_INSERT;
SELECT COUNT(*) INTO V_SITEPLOTIMG_COUNT FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'SitePlot' AND IMG_ID = TBL_INSERT;
IF V_PANAROMICIMG_COUNT > 0 THEN
BEGIN
DELETE FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Panaromic' AND IMG_ID = TBL_INSERT;
END;
END IF;
IF V_SATELLITEIMG_COUNT > 0 THEN
BEGIN
DELETE FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Satellite' AND IMG_ID = TBL_INSERT;
END;
END IF;
IF V_SITEPLOTIMG_COUNT > 0 THEN
BEGIN
DELETE FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'SitePlot' AND IMG_ID = TBL_INSERT;
END;
END IF;
FOR PMULTIFIELDS IN (SELECT REGEXP_SUBSTR(P_PANORAMIC_IMAGES,'[^,]+', 1, LEVEL) AS IMAGES FROM DUAL
CONNECT BY REGEXP_SUBSTR(P_PANORAMIC_IMAGES, '[^,]+', 1, LEVEL) IS NOT NULL
)
LOOP
INSERT INTO TBL_VSAT_IMAGE_DETAIL
(
IMG_ID,
IMG_NAME,
IMG_TYPE,
IMG_UPLOADED_DATE,
UPLOADED_BY
)
VALUES
(
TBL_INSERT,
PMULTIFIELDS.IMAGES,
'Panaromic',
SYSDATE,
P_CREATEDBY
);
END LOOP;
FOR PSATELLITEIMG IN (SELECT REGEXP_SUBSTR(P_SATELLITE_IMAGES,'[^,]+', 1, LEVEL) AS IMAGES FROM DUAL
CONNECT BY REGEXP_SUBSTR(P_SATELLITE_IMAGES, '[^,]+', 1, LEVEL) IS NOT NULL
)
LOOP
INSERT INTO TBL_VSAT_IMAGE_DETAIL
(
IMG_ID,
IMG_NAME,
IMG_TYPE,
IMG_UPLOADED_DATE,
UPLOADED_BY
)
VALUES
(
TBL_INSERT,
PSATELLITEIMG.IMAGES,
'Satellite',
SYSDATE,
P_CREATEDBY
);
END LOOP;
IF P_SITEPLOT_IMAGES IS NOT NULL THEN
BEGIN
INSERT INTO TBL_VSAT_IMAGE_DETAIL
(
IMG_ID,
IMG_NAME,
IMG_TYPE,
IMG_UPLOADED_DATE,
UPLOADED_BY
)
VALUES
(
TBL_INSERT,
P_SITEPLOT_IMAGES,
'SitePlot',
SYSDATE,
P_CREATEDBY
);
END;
END IF;
END;
END IF;
COMMIT;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
NOTE
While updating the record my TBL_INSERT returns as NULL
Expanding on what #user7294900 pointed you towards... in the declare section you have:
V_VSAT_DETAIL_ID NUMBER:=0;
then if v_count > 0 you do:
SELECT VSAT_DETAIL_ID INTO TBL_INSERT FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
UPDATE TBL_VSAT_MST_DETAIL SET
CIRCLE = P_CIRCLE,
CONTACT_DETAILS = P_CONTACT_DETAILS,
SITE_TYPE = P_SITETYPE,
SITE_DETAILS_DIMENSION = P_SITE_PLOT_DIMENSION,
SITE_DETAILS_TECHNOLOGY = P_TECHNOLOGY
WHERE VSAT_DETAIL_ID = V_VSAT_DETAIL_ID
RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
The select is setting TBL_INSERT to the ID value from your table. But when you do the update your filter is using V_VSAT_DETAIL_ID, which is still set to its initial value of zero.
You probably meant to do:
SELECT VSAT_DETAIL_ID INTO V_VSAT_DETAIL_ID FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
although you could still with your current select, and use that in the update too (making the returning into a bit redundant.
Be aware though that if v_count is not exactly 1, i.e. you have more than one row matching the P_SAPID and P_CANDIDATEID values, the select will get a too-many-rows exception. You won't see that because you are silently squashing any errors you get at run time.
It's usually not a good idea to commit or rollback inside a procedure anyway; it should be up to the caller to decide what to do, as this could be one of a series of statements and calls that you really want to treat as an atomic transaction. (You may be interested in savepoints.)
If you really, really want to rollback on exception within the procedure, you should at least re-raise the exception so the caller knows there was a problem:
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
RAISE;
END INSERT_INTO_VSAT_MST_DATA;
but I would avoid when others if you can.
You could also combine a few steps by getting the ID at the start (again kind of assuming there is at most one matching row), and dropping the separate count and v_count variable:
SELECT MAX(VSAT_DETAIL_ID) INTO V_VSAT_DETAIL_ID
FROM TBL_VSAT_MST_DETAIL
WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
IF V_VSAT_DETAIL_ID IS NOT NULL THEN
UPDATE TBL_VSAT_MST_DETAIL SET
CIRCLE = P_CIRCLE,
CONTACT_DETAILS = P_CONTACT_DETAILS,
SITE_TYPE = P_SITETYPE,
SITE_DETAILS_DIMENSION = P_SITE_PLOT_DIMENSION,
SITE_DETAILS_TECHNOLOGY = P_TECHNOLOGY
WHERE VSAT_DETAIL_ID = V_VSAT_DETAIL_ID
RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
ELSE
...
And I'm not sure why you're doing counts before your deletes later on, and it looks like all your tbl_insert references could/should be v_vast_detail_id - there doesn't seem an obvious reason to have two variables for that. Passing in a comma-delimited string that you then have to tokenize is also a bit painful - you should consider passing in a collection of values instead, if whatever calls this can manage that.
As also pointed out, you could use merge instead of the separate insert/update logic.
You don't assign value to V_VSAT_DETAIL_ID which is used in your update as a key.
You should use merge for this kind of operations

Why does this get-or-create function never seem to insert?

I'm writing a function for batch-importing data into our org-chart. It seems to work fine for retrieving entries that already exists, but when an entry does not already exist, and it's supposed to insert, commit, and return the result of a re-attempt (so as to get the auto-generated ID), it always returns NULL.
I'm sure I'm doing something wrong here, but what? Help appreciated.
Note: there's a before-insert trigger that fills in DEPT_ID if it's not specified. Works fine if the insert statement is executed by hand.
CREATE TABLE DEPTS
(
"DEPT_ID" VARCHAR2(10 BYTE),
"HEADER_ID" VARCHAR2(10 BYTE),
"COMMENTS" VARCHAR2(100 BYTE),
"CATEGORY" VARCHAR2(2 BYTE)
);
CREATE OR REPLACE FUNCTION get_or_make_unit(
, in_cat VARCHAR2
, in_cmt VARCHAR2
, in_hdr VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
IF unit_id IS NULL THEN
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
RETURN unit_id;
ELSE
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
END IF;
END get_or_make_unit;
And the trigger:
CREATE OR REPLACE TRIGGER HRD.DEPTS_BIR
BEFORE INSERT
ON HRD.DEPTS
FOR EACH ROW
DECLARE
JML NUMBER;
BEGIN
SELECT SEQ_DEPT_ID.NEXTVAL INTO JML FROM DUAL;
:NEW.DEPT_ID:='D'||to_char(JML);
END DEPTS_BIR;
Examples
This works:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
'D532'
, 'ACCOUNTING'
, '2');
COMMIT;
SELECT get_or_make_unit('2', 'ACCOUNTING', 'D532') FROM DUAL;
=> 'D533'
This does not:
SELECT get_or_make_unit('2', 'NEW DEPT', 'D532') FROM DUAL;
=> NULL
Instead of:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
use RETURNING INTO:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat) RETURNING dept_id INTO unit_id;
COMMIT;
I think recursive call in not the best approach, but if you are strict of using it, then please post definition of mentioned before-insert trigger.
UPDATE: You cannot call functions containing DML operations from SQL statement. Please see this answer for details. Example of correct call:
DECLARE
unit_id varchar2(32);
BEGIN
unit_id := get_or_make_unit('2', 'NEW DEPT', 'D532');
dbms_output.put_line(unit_id);
END;
UPDATE2: Also you need to catch NO_DATA_FOUND exception that is raised when you call your function with not existent combination. Below is example:
CREATE OR REPLACE FUNCTION get_or_make_unit(in_cat VARCHAR2,
in_cmt VARCHAR2,
in_hdr VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
exception
when NO_DATA_FOUND then
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts
(header_id, comments, category)
VALUES
(in_hdr, in_cmt, in_cat)
returning dept_id into unit_id;
COMMIT;
RETURN unit_id;
END get_or_make_unit;
Your call to get_or_make_unit appears to be missing the first parameter.
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
This code is not inserting anything into dept_id of depts table. Thats the reason you are getting null

Resources