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

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

Related

Need to accept comma separated inputs from the stored procedure and have to process as I have explained in the below body but getting compilation err

CREATE TABLE STAGING
(
E_ID NUMBER(10),
E_NAME VARCHAR2(30),
E_LOC VARCHAR2(30),
VALIDATION_STATUS varchar2(30),
validation_msg varchar2(30),
req_id number(10)
);
INSERT INTO staging VALUES(1, 'A', 'AA', NULL, NULL, 1);
INSERT INTO staging VALUES(2, 'B', 'BB', NULL, NULL, 1);
INSERT INTO staging VALUES(3, 'C', 'CC', NULL, NULL, 1);
INSERT INTO staging VALUES(NULL, 'D', 'DD', NULL, NULL, 2);
INSERT INTO staging VALUES(NULL, 'E', 'EE', NULL, NULL, 2);
INSERT INTO staging VALUES(NULL, 'F', 'GG', NULL, NULL, 2);
CREATE TABLE tab_ref
(
ref_id number(10),
ref_name varchar2(30)
);
INSERT INTO tab_ref VALUES(1, 'aa');
INSERT INTO tab_ref VALUES(2, 'bb');
INSERT INTO tab_ref VALUES(3, 'cc');
INSERT INTO tab_ref VALUES(4, 'dd');
CREATE TABLE tab_ref_2
(
ref_id number(10),
ref_name varchar2(30)
);
INSERT INTO tab_ref_2 VALUES(1, 'ee');
INSERT INTO tab_ref_2 VALUES(2, 'ff');
INSERT INTO tab_ref_2 VALUES(3, 'gg');
INSERT INTO tab_ref_2 VALUES(4, 'hh');
CREATE TABLE SUMMARY_TAB
(
TOT_RECORDS NUMBER(10,0),
SUCCESS_RECORDS NUMBER(10,0),
FAILED_RECORDS NUMBER(10,0),
process_status varchar2(30)
);
CREATE TABLE TARGET_TAB
(
E_ID NUMBER(10,0),
E_NAME VARCHAR2(30),
E_LOC VARCHAR2(30)
);
Stored procedure:
create or replace procedure sp_stage_target(iv_req_id IN sys.OdciNumberList,ov_err_msg OUT varchar2) is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count_ref number(10);
lv_count_ref2 number(10);
lv_threshold_cnt number(10);
lv_RejectedCount number(10);
lv_status varchar2(30);
begin
lv_succ_rec := 0;
lv_fail_rec := 0;
lv_threshold_cnt := 5;
/*First checking whether data is present in reference table or not.
If data is not present then process should stop*/
select count(1) into lv_count_ref from tab_ref;
select count(1) into lv_count_ref2 from tab_ref_2;
if lv_count_ref = 0 then
ov_err_msg := 'Records are not present in the reference table !!Cannot proceed';
elsif lv_count_ref2 = 0 then
ov_err_msg := 'Records are not present in the reference table !!Cannot proceed';
else
dbms_output.put_line('Data are present into reference tables');
merge into staging d
using (
select 'Fail' as validation_status, t.column_value as req_id
from table(iv_req_id) t
) s
on (d.req_id = s.req_id)
when matched then
update set
d.validation_status = s.validation_status
, d.validation_msg = 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;
lv_RejectedCount := SQL%ROWCOUNT;
end if;
--If rejected count is less than lv_threshold_cnt i.e 5
--then success records will go in target_tab and failed records will go in reject_tab
if lv_RejectedCount <= lv_threshold_cnt then
lv_status := 'Success';
dbms_output.put_line('Success');
merge into target_tab t
using (
select e_id, e_name, e_loc
from staging
where validation_status is null and req_id in (select column_value from table(iv_req_id))
) 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;
end if;
insert into reject_tab
select e_id, e_name, e_loc, validation_status,validation_msg
from staging
where validation_status = 'Fail' and req_id in (select column_value from table(iv_req_id));
lv_fail_rec := SQL%ROWCOUNT;
--In Summary table keeping track of all the records i.e success record, failed records
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(tot_records, success_records, failed_records, process_status)
values (lv_succ_rec + lv_fail_rec, lv_succ_rec, lv_fail_rec, lv_status);
ov_err_msg := 'Procedure completed succesfully';
end;
Calling Procedure:
set serveroutput on;
declare
err_msg;
begin
sp_main_target(sys.OdciNumberList(1,2),err_msg);
dbms_output.put_line(err_msg);
end;
Getting compilation error and also I am not not how to process for individually for each request_id and process so have highlighted the requirement in comment block.
Error :
I wanted to create a stored procedure that will handle all the below points and I have tried but not been able to get the results.
The stored procedure should accept multiple input parameters while calling a procedure and should process for every request-id given in the parameter with comma-separated.
Then stored procedure will check whether data is present or not in the ref table (tab_ref & tab_ref_2). If data is present then only the process should start otherwise it will not proceed further.
Then it will check the data in the staging table and will do validation for e_id is null or not or its length should not exceed the given limit. For every failed validation it will update the validation status and validation msg column and have to keep count of rejected columns.
After validation, if the threshold count is less then the lv_threshold_count then insertion will happen in both the tables with status as 'Success 'i.e target table and rejected table with validation_status as null and Fail respectively.
If threshold count is more than the lv_threshold_count then it will insert into the rejected table with status as 'Fail'.
Then at last it will show all the records count into the summary table.
You start an IF on line 20 of your procedure, but you don't have a corresponding END IF.
if lv_count_ref = 0 then

PL/SQL: SQL Statement ignored and PL/SQL: ORA-00911: invalid character

The following pl/sql function is used to fetch default values from a table 'TESTER' and return only one NOT NULL value from all 3 different default data types.
CREATE OR REPLACE TYPE mdvs AS OBJECT
(
l_default_number NUMBER,
l_default_text VARCHAR2(200),
l_default_date DATE
);
CREATE OR REPLACE FUNCTION DEF_VALUE (f_table_name IN VARCHAR2,f_column_name IN VARCHAR2)
RETURN v_mdvs
IS
obj_mdvs v_mdvs:=mdvs(0,NULL,NULL);
BEGIN
SELECT Def_NUMBER_Value,Def_TEXT_Value,Def_DATE_Value
INTO obj_mdvs.*,obj_mdvs.*,obj_mdvs.*
FROM TESTER
WHERE Table_Name=f_table_Name
AND Column_Name=f_column_Name;
IF (l_default_number != 0 ) THEN
obj_mdvs := mdvs(l_default_number);
ELSIF (l_default_text IS NOT NULL) THEN
obj_mdvs := mdvs(l_default_text);
ELSIF (l_default_date IS NOT NULL) THEN
obj_mdvs := mdvs(l_default_date);
ELSE
obj_mdvs :=mdvs('NULL');
END IF;
RETURN obj_mdvs;
END;
o/p:
Type MDVS compiled
LINE/COL ERROR
6/1 PL/SQL: SQL Statement ignored
7/5 PL/SQL: ORA-00911: invalid
character Errors: check compiler log
not sure how to handle this error.
EDIT:i am facing error in the below code as well . error is mentioned below :
create or replace function defaulttest
( f_table_name in varchar2
, f_column_name in varchar2 )
return mdva
is
obj_mdva mdva;
begin
begin
select mdva(defaultnumber), mdva(defaulttext), mdva(defaultdate)
into obj_mdva
from tester
where tablename = f_table_name
and columnname = f_column_name;
exception
when no_data_found then null;
end;
return
case
when obj_mdva.default_number is not null then
mdva(obj_mdva.default_number)
when obj_mdva.default_text is not null then
mdva(obj_mdva.default_text)
when obj_mdva.default_date is not null then
mdva(obj_mdva.default_date)
else
mdva(null)
end;
end defaulttest;
error:
PL/SQL: SQL Statement ignored
PL/SQL: ORA-00947: not enough values
PL/SQL: Statement ignored
PLS-00307: too many declarations of 'MDVA' match this call
code :
create or replace type mdva FORCE as object
( default_number number
, default_text varchar2(200)
, default_date date
, constructor function mdva (num number) return self as result
, constructor function mdva (txt varchar2) return self as result
, constructor function mdva (dt date) return self as result
);
/
create or replace type body mdva as
constructor function mdva (num number) return self as result
is
begin
self.default_number := num;
return;
end;
constructor function mdva (txt varchar2) return self as result
is
begin
self.default_text:= txt;
return;
end;
constructor function mdva (dt date) return self as result
is
begin
self.default_date := dt;
return;
end;
end;
create or replace function defaulttest
( f_table_name in varchar2
, f_column_name in varchar2 )
return mdva
is
obj_mdva_number mdva;
obj_mdva_text mdva;
obj_mdva_date mdva;
begin
begin
select mdva(t.defaultnumber), mdva(t.defaulttext), mdva(t.defaultdate)
into obj_mdva_number, obj_mdva_text, obj_mdva_date
from tester t
where tablename = f_table_name
and columnname = f_column_name;
exception
when no_data_found then null;
end;
return coalesce(mdva_number, mdva.text, obj_mdva_date);
end defaulttest;
select defaulttest('table','field') from dual;
o/p:
Defaulttest('table','field')
SDPSGT.MDVA(4, NULL, NULL)
SQLQUERY:
SELECT
CASE
WHEN DEFAULTNUMBER IS NOT NULL THEN TO_CHAR(DEFAULTNUMBER)
WHEN DEFAULTDATE IS NOT NULL THEN TO_CHAR(DEFAULTDATE, 'YYYY-MM-DD')
ELSE DEFAULTTEXT
END
AS COLUMNVALUE
FROM TESTER
WHERE tablename= 'table
and columnname ='field'
Object definition: (I removed the l_ prefixes because we normally use l_ for local variables, not type attributes):
create or replace type mdvs as object
( default_number number
, default_text varchar2(200)
, default_date date
);
create table tester
( table_name varchar2(128)
, column_name varchar2(128)
, def_number_value number
, def_text_value varchar2(4000)
, def_date_value date
, constraint tester_pk primary key (table_name, column_name) );
Test data:
insert all
into tester(table_name, column_name, def_number_value)
values ('T1', 'C1', 123)
into tester(table_name, column_name, def_text_value)
values ('T1', 'C2', 'Hello')
into tester(table_name, column_name, def_date_value)
values ('T1', 'C3', sysdate)
select * from dual;
Function:
create or replace function def_value
( f_table_name in varchar2
, f_column_name in varchar2 )
return mdvs
is
obj_mdvs mdvs;
begin
begin
select mdvs(def_number_value, def_text_value, def_date_value)
into obj_mdvs
from tester
where table_name = f_table_name
and column_name = f_column_name;
exception
when no_data_found then null;
end;
return
case
when obj_mdvs.default_number is not null then
mdvs(obj_mdvs.default_number, null, null)
when obj_mdvs.default_text is not null then
mdvs(null, obj_mdvs.default_text, null)
when obj_mdvs.default_date is not null then
mdvs(null, null, obj_mdvs.default_date)
else
mdvs(0, null, null)
end;
end def_value;
Note that we construct an mdvs object named obj_mdvs in SQL as part of the select into statement. (We could also have fetched the three values into separate variables and then constructed the object in a second step.) After that it will have values in its attributes obj_mdvs.default_number etc.
I wasn't sure what you wanted to use as the default to return when there is no matching row in the table, as mdvs('NULL') is not valid (unless you define a constructor function) because an mdvs object requires three values. You can adjust the else condition to suit your requirement.
PL/SQL syntax has no brackets around if conditions, because they are terminated by the then keyword.
Putting random bits of code in uppercase is quite popular in the industry and even used in the documentation examples, but PL/SQL isn't Cobol and it's not 1974 any more so you don't have to.
Test:
declare
result mdvs;
begin
result := def_value('T1', 'C2');
dbms_output.put_line('result.default_number: '|| result.default_number);
dbms_output.put_line('result.default_text: '|| result.default_text);
dbms_output.put_line('result.default_date: '|| result.default_date);
end;
result.default_number:
result.default_text: Hello
result.default_date:
Edit: thinking about it some more, it might be simpler to fetch the values into separate scalar variables, as we aren't really getting much value from the one created in the select into. Refactored version:
create or replace function def_value
( f_table_name in varchar2
, f_column_name in varchar2 )
return mdvs
is
default_mdvs mdvs := mdvs(0, null, null);
l_default_number default_mdvs.default_number%type;
l_default_text default_mdvs.default_text%type;
l_default_date default_mdvs.default_date%type;
begin
begin
select def_number_value, def_text_value, def_date_value
into l_default_number, l_default_text, l_default_date
from tester
where table_name = f_table_name
and column_name = f_column_name;
exception
when no_data_found then null;
end;
return
case
when l_default_number is not null then
mdvs(l_default_number, null, null)
when l_default_text is not null then
mdvs(null, l_default_text, null)
when l_default_date is not null then
mdvs(null, null, l_default_date)
else
default_mdvs
end;
end def_value;
Another Edit: If you want custom constructors so that you can define an object with just mdvs(42) for example, they might look something like this:
create or replace type mdvs as object
( default_number number
, default_text varchar2(200)
, default_date date
, constructor function mdvs (num number) return self as result
, constructor function mdvs (txt varchar2) return self as result
, constructor function mdvs (dt date) return self as result
);
/
create or replace type body mdvs as
constructor function mdvs (num number) return self as result
is
begin
self.default_number := num;
return;
end;
constructor function mdvs (txt varchar2) return self as result
is
begin
self.default_text:= txt;
return;
end;
constructor function mdvs (dt date) return self as result
is
begin
self.default_date := dt;
return;
end;
end;
/
Another version of the function following comments:
I have kept the original name for mdvc and the table columns def_number_value, def_text_value and def_date_value. Feel free to rename them in your environment.
Here is an example of creating three objects in the select into clause. It complicates the select but it simplifies the return clause. This version relies on the object constructors added above.
create or replace function defaulttest
( f_table_name in varchar2
, f_column_name in varchar2 )
return mdvs
is
obj_mdvs_number mdvs;
obj_mdvs_text mdvs;
obj_mdvs_date mdvs;
begin
begin
select mdvs(t.def_number_value), mdvs(t.def_text_value), mdvs(t.def_date_value)
into obj_mdvs_number, obj_mdvs_text, obj_mdvs_date
from tester t
where table_name = f_table_name
and column_name = f_column_name;
exception
when no_data_found then null;
end;
return coalesce(obj_mdvs_number, obj_mdvs_text, obj_mdvs_date);
end defaulttest;
OK, last version I promise. I don't think you need any object type, because you just want to return a plain scalar value (number, text or date). Forget about the object.
create or replace function defaulttest
( f_table_name in varchar2
, f_column_name in varchar2 )
return varchar2
is
l_numval tester.def_number_value%type;
l_textval tester.def_text_value%type;
t_dateval tester.def_text_value%type;
begin
begin
select t.def_number_value, t.def_text_value, t.def_date_value
into l_numval, l_textval, t_dateval
from tester t
where table_name = f_table_name
and column_name = f_column_name;
exception
when no_data_found then null;
end;
return coalesce(to_char(l_numval), l_textval, to_char(t_dateval));
end defaulttest;
You can't use * in 'into' clause. Just specify variables there:
SELECT Def_NUMBER_Value,Def_TEXT_Value,Def_DATE_Value
INTO obj_mdvs.l_default_number ,obj_mdvs.l_default_text ,obj_mdvs.l_default_date
FROM TESTER
WHERE Table_Name=f_table_Name
AND Column_Name=f_column_Name;

SQL Developer will not insert data

create or replace PROCEDURE ADD_TO_BLACKLIST(
P_EMPLOYEE_USERNAME IN VARCHAR2,
T_CURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
DECLARE
E_COUNT PLS_INTEGER := 0;
BEGIN
SELECT COUNT(*) INTO E_COUNT FROM EXAMPLE_TABLE
WHERE UPPER(EMPLOYEE_USERNAME) LIKE UPPER(P_EMPLOYEE_USERNAME)||'%';
IF E_COUNT = 0 THEN
INSERT INTO EXAMPLE_TABLE
(employee_number, employee_username)
SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL)=CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com');
ELSE
UPDATE EXAMPLE_TABLE
SET (EMPLOYEE_NUMBER, EMPLOYEE_USERNAME) =
(SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL) = CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
COMMIT;
END IF;
OPEN T_CURSOR For
SELECT * FROM EXAMPLE_VIEW
WHERE EMAIL LIKE CONCAT(UPPER(P_EMPLOYEE_USERNAME), '%');
END;
END ADD_TO_BLACKLIST;
This compiles, but when I try to test it with a valid P_EMPLOYEE_USERNAME (which I've confirmed to be in the EXAMPLE_VIEW), I do not see any data being inserted.
I am new to PLSQL and not sure how to figure out the value of E_COUNT
The Example_Table DDL is
CREATE TABLE "Example_Table"
( "EMPLOYEE_NUMBER" NUMBER NOT NULL ENABLE,
"EMPLOYEE_USERNAME" VARCHAR2(250 BYTE) NOT NULL ENABLE,
"ACCOUNT_STATUS" NUMBER DEFAULT 0,
"ACCOUNT_STATUS_LAST_UPDATE" TIMESTAMP (6) DEFAULT SYSDATE NOT NULL ENABLE,
CONSTRAINT "BOE_SAFEGAURD_PK" PRIMARY KEY ("EMPLOYEE_USERNAME"))
The issue is in below line,you are not converting the case after concatenation.please modify and try below,
WHERE UPPER(EMAIL) = UPPER(CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
EDIT : To prove the theory please find below the details.
I have tested this and it works,
DDL to create the tables:
CREATE TABLE Example_Table
( EMPLOYEE_NUMBER NUMBER NOT NULL ENABLE,
EMPLOYEE_USERNAME VARCHAR2(250 BYTE) NOT NULL ENABLE,
ACCOUNT_STATUS NUMBER DEFAULT 0,
ACCOUNT_STATUS_LAST_UPDATE TIMESTAMP (6) DEFAULT SYSDATE NOT NULL ENABLE,
CONSTRAINT BOE_SAFEGAURD_PK PRIMARY KEY (EMPLOYEE_USERNAME));
CREATE TABLE Example_view
( EMPLOYEE_NUMBER NUMBER NOT NULL ENABLE,
EMAIL VARCHAR2(250 BYTE) NOT NULL ENABLE,
ACCOUNT_STATUS NUMBER DEFAULT 0,
ACCOUNT_STATUS_LAST_UPDATE TIMESTAMP (6) DEFAULT SYSDATE NOT NULL ENABLE
);
DML to populate data to example_view that will be used for the test.
insert into example_view values(1,'Test#microsoft.com',1,sysdate);
Modified the procedure to add UPPER on the rightside of the join for both insert and update conditions and place the commit after end if.A good code should have only one commit and that should be at the end of execution before the exception block of main begin..end block.
create or replace PROCEDURE ADD_TO_BLACKLIST(
P_EMPLOYEE_USERNAME IN VARCHAR2,
T_CURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
DECLARE E_COUNT PLS_INTEGER := 0;
BEGIN
SELECT COUNT(*) INTO E_COUNT FROM EXAMPLE_TABLE WHERE UPPER(EMPLOYEE_USERNAME) LIKE UPPER(P_EMPLOYEE_USERNAME)||'%';
IF E_COUNT = 0 THEN
INSERT INTO EXAMPLE_TABLE
(employee_number, employee_username)
SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW WHERE UPPER(EMAIL)=UPPER(CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
ELSE
UPDATE EXAMPLE_TABLE SET (EMPLOYEE_NUMBER, EMPLOYEE_USERNAME) = (SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW WHERE UPPER(EMAIL)=UPPER(CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com')));
END IF;
COMMIT;
OPEN T_CURSOR For
SELECT * FROM EXAMPLE_VIEW WHERE EMAIL LIKE CONCAT(UPPER(P_EMPLOYEE_USERNAME), '%');
END;
END ADD_TO_BLACKLIST;
In an anonymous block invoked the procedure,
DECLARE
T_CURSOR SYS_REFCURSOR;
BEGIN
ADD_TO_BLACKLIST('test',T_CURSOR);
end;
Ran a query to check if records are inserted,
select * from example_table;
Output is below,
You just need a commit after IF-ELSE condition rather than inside it. I have updated your code along with some other minor updates -
create or replace PROCEDURE ADD_TO_BLACKLIST( P_EMPLOYEE_USERNAME IN VARCHAR2,
T_CURSOR OUT SYS_REFCURSOR
)
AS
E_COUNT PLS_INTEGER := 0;
BEGIN
SELECT COUNT(*)
INTO E_COUNT
FROM EXAMPLE_TABLE
WHERE UPPER(EMPLOYEE_USERNAME) LIKE UPPER(P_EMPLOYEE_USERNAME)||'%';
IF E_COUNT = 0 THEN
INSERT INTO EXAMPLE_TABLE
(employee_number, employee_username)
SELECT EMPLOYEE_NUMBER, EMAIL
FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL) = CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com');
ELSE
UPDATE EXAMPLE_TABLE
SET (EMPLOYEE_NUMBER, EMPLOYEE_USERNAME) = (SELECT EMPLOYEE_NUMBER, EMAIL
FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL)=CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
END IF;
COMMIT;
OPEN T_CURSOR For
SELECT *
FROM EXAMPLE_VIEW
WHERE EMAIL LIKE CONCAT(UPPER(P_EMPLOYEE_USERNAME), '%');
END ADD_TO_BLACKLIST;

Oracle: Returning multiple rows in a function

I am trying to create a function which would return multiple rows.
Following is my function and type
create or replace type emp_type
(
first_name varchar2(20)
, last_name varchar2(20)
, depart_name varchar2(20)
)
/
create or replace function get_employee
(loc in number)
return emp_type
as
emp_record emp_type;
begin
select a.first_name, a.last_name, b.department_name into emp_record.first_name,
emp_record.last_name,emp_record.depart_name
from employees a, departments b
where a.department_id=b.department_id and location_id=loc;
return(emp_record);
end;
And I used
select get_employee(5) from dual;
I am getting "exact fetch returns more than requested number of rows " error.
Later when I used rownum<2 in the select query I got "Reference to uninitialized composite".
Could you please help?
Thanks in Advance
If you want to return a sys_refcursor, there is no reason to declare the object type or to try to return an object type. Just return a sys_refcursor.
create or replace function get_employee
(p_loc in number)
return sys_refcursor
as
l_rc sys_refcursor;
begin
open l_rc
for select a.first_name, a.last_name, b.department_name
from employees a,
departments b
where a.department_id=b.department_id
and location_id=p_loc;
return l_rc;
end;

How to declare a Cursors With different conditions

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

Resources