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
Related
I have a table named emp with name, id, salary as column names.
My procedure takes id as input and it can be more than one id also. like('1','2','3',...)
Required: For each and every id the values should be returned as XML.
Supoose: For id=1 and id=2 as input my output should be
<row>
<name>name1</name>
<salary>salary1</salary>
</row>
<row>
<name>name2</name>
<salary>salary2</salary>
</row>
I am able to run the code to separate id's and get the output. But I am getting stuck at sending values as xml.
Code i tried:
procedure get_details(P_ID varchar2) as
begin
select * from emp where id in (
select regexp_substr(P_ID,'[^,]+', 1, level) from dual
connect BY regexp_substr(P_ID, '[^,]+', 1, level)
is not null);
end get_values;
Any input will be appreciated.
Edit regarding solutions: Both the solutions provided in db-fiddle are working perfectly fine.
Solution 1:
CREATE PROCEDURE get_details(
P_IDs IN varchar2
) IS
v_clob CLOB;
BEGIN
SELECT XMLELEMENT(
"root",
XMLAGG(
XMLELEMENT(
"row",
XMLFOREST(
name AS "name",
salary AS "salary"
)
)
)
).getClobVal()
INTO v_clob
FROM emp
where p_ids LIKE '%''' || id || '''%';
DBMS_OUTPUT.PUT_LINE( v_clob );
end get_details;
/
BEGIN
DBMS_OUTPUT.PUT_LINE( 'Your output:' );
get_details( '''1'',''3''' );
END;
/
Solution 2:
create or replace procedure get_details(p_id varchar2)
as
lo_xml_output clob;
begin
select to_clob(xmltype(cursor(select *
from emp
where id in
(select regexp_substr(p_id,'[^,]+', 1, level)
from dual
connect by regexp_substr(P_ID, '[^,]+', 1,level) is
not null))))
into lo_xml_output from dual;
DBMS_OUTPUT.PUT_LINE( lo_xml_output );
end get_details;
/
BEGIN
DBMS_OUTPUT.PUT_LINE('');
get_details('1,3');
END;
/
You can use the XML functions:
CREATE PROCEDURE get_details(
P_IDs IN varchar2
) IS
v_clob CLOB;
BEGIN
SELECT XMLELEMENT(
"root",
XMLAGG(
XMLELEMENT(
"row",
XMLFOREST(
name AS "name",
salary AS "salary"
)
)
)
).getClobVal()
INTO v_clob
FROM emp
where p_ids LIKE '%''' || id || '''%';
DBMS_OUTPUT.PUT_LINE( v_clob );
end get_details;
/
So, for the test data:
CREATE TABLE emp ( id, name, salary ) AS
SELECT 1, 'name1', 1000 FROM DUAL UNION ALL
SELECT 2, 'name2', 2000 FROM DUAL UNION ALL
SELECT 3, 'name3', 3000 FROM DUAL
The anonymous block:
BEGIN
get_details( '''1'',''3''' );
END;
/
Outputs:
<root><row><name>name1</name><salary>1000</salary></row><row><name>name3</name><salary>3000</salary></row></root>
db<>fiddle here
Another way will be by using xmltype,you can pass a query as a cursor to xmltype.
create or replace procedure get_details(p_id varchar2)
as
lo_xml_output clob;
begin
select to_clob(xmltype(cursor(select *
from emp
where id in
(select regexp_substr(p_id,'[^,]+', 1, level)
from dual
connect by regexp_substr(P_ID, '[^,]+', 1,level) is
not null))))
into lo_xml_output from dual;
DBMS_OUTPUT.PUT_LINE( lo_xml_output );
end get_details;
/
-- test
BEGIN
DBMS_OUTPUT.PUT_LINE('');
get_details('1,3');
END;
/
Db<>fiddle for your reference
P.S. we can also usedbms_xmlgen.getxml but then we need to build the SQL dynamically before passing it to dbms_xmlgen.getxml. you can see in the fiddle as well.
I have a stored procedure like below where multiple employee IDs will be passed as comma-separated value (multiple IDs). It is throwing error as "ORA-01722: invalid number". I know it's because of passing varchar2 variable for the numeric ID column. But is there any way we can achieve this simply?
create or replace PROCEDURE Fetch_Emp_Name(Emp_id in varchar2)
IS
BEGIN
select Name from EMP where id in (emp_id);
END;
You can use dynamic sql.
create or replace PROCEDURE Fetch_Emp_Name(emp_id in varchar2) IS
v_result varchar2;
begin
execute immediate
'select Name from EMP where id in (' || 'emp_id' || ')'
into
v_result;
end;
Also you can use package dbms_sql for dynamic sql.
Update
Another approach. I think may be better.
create or replace PROCEDURE Fetch_Emp_Name(emp_id in varchar2) IS
v_result varchar2;
begin
select
Name
from
EMP
where
id in
(
select
to_number(regexp_substr(emp_id, '[^,]+', 1, level))
from
dual
connect by regexp_substr(emp_id, '[^,]+', 1, level) is not null
);
exception
when no_data_found then
-- error1;
when too_many_rows then
-- error2;
end;
Sorry for before, I did not get the question in the right way. If you get a lot of IDs as different parameters, you could retrieve the list of names as an string split by comma as well. I put this code where I handled by regexp_substr the name of different emp_ids you might enter in the input parameter.
Example ( I am assuming that the IDs are split by comma )
create or replace PROCEDURE Fetch_Emp_Name(p_empid in varchar2) IS
v_result varchar2(4000);
v_append emp.name%type;
v_emp emp.emp_id%type;
counter pls_integer;
i pls_integer;
begin
-- loop over the ids
counter := REGEXP_COUNT(p_empid ,'[,]') ;
--dbms_output.put_line(p_empid);
if counter > 0
then
i := 0;
for r in ( SELECT to_number(regexp_substr(p_empid,'[^,]+',1,level)) as mycol FROM dual CONNECT BY LEVEL <= REGEXP_COUNT(p_empid ,'[,]')+1 )
loop
--dbms_output.put_line(r.mycol);
v_emp := r.mycol ;
select name into v_append from emp where emp_id = v_emp;
if i < 1
then
v_result := v_append ;
else
v_result := v_result ||','|| v_append ;
end if;
i := i + 1;
end loop;
else
v_emp := to_number(p_empid);
select name into v_result from emp where emp_id = v_emp;
end if;
dbms_output.put_line(v_result);
exception
when no_data_found
then
raise_application_error(-20001,'Not Employee found for '||v_emp||' ');
when too_many_rows
then
raise_application_error(-20002,'Too many employees for id '||v_emp||' ');
end;
Test
SQL> create table emp ( emp_id number, name varchar2(2) ) ;
Table created.
SQL> insert into emp values ( 1 , 'AA' );
1 row created.
SQL> insert into emp values ( 2 , 'BB' ) ;
1 row created.
SQL> commit;
SQL> insert into emp values ( 3 , 'CC' ) ;
1 row created.
SQL> select * from emp ;
EMP_ID NA
---------- --
1 AA
2 BB
3 CC
SQL> exec Fetch_Emp_Name('1') ;
AA
PL/SQL procedure successfully completed.
SQL> exec Fetch_Emp_Name('1,2,3') ;
AA,BB,CC
PL/SQL procedure successfully completed.
SQL>
I want update a table using a dynamic query, cursor and bulk collect. But I don't know the syntax:
declare
my_cursor ?;
-- other objects;
begin
execute immediate
"select s.col1, s.col2, rowid, d.rowid
from source_table s, destination_table d
where s.id = d.id "
BULK COLLECT INTO my_cursor;
FORALL i IN my_cursor.FIRST..my_cursor.LAST
UPDATE destination_table set col_a=my_cursor(i).col1 , col_b=my_cursor(i).col2
WHERE rowid = my_cursor(i).rowid;
commit;
end;
what would be the correct syntax and oracle objects
please help.
You can use something like this:
declare
type REC_TYPE is record (
ID SOURCE_TABLE.ID%type,
COL1 SOURCE_TABLE.COL1%type,
COL2 SOURCE_TABLE.COL2%type
);
type REC_CURSOR is ref cursor;
ref_cursor REC_CURSOR;
rec REC_TYPE;
sql_query VARCHAR2(4000);
begin
sql_query := 'select s.ID, COL1, COL2 from SOURCE_TABLE s, DESTINATION_TABLE d where s.ID = d.ID';
open ref_cursor for sql_query;
loop
fetch ref_cursor into rec;
exit when ref_cursor%NOTFOUND;
update DESTINATION_TABLE
set COL_A = rec.COL1, COL_B = rec.COL2
WHERE ID = rec.ID;
end loop;
close ref_cursor;
commit;
end;
/
or with bulk collect:
declare
type REC_TYPE is record (
ID SOURCE_TABLE.ID%type,
COL1 SOURCE_TABLE.COL1%type,
COL2 SOURCE_TABLE.COL2%type
);
type REC_TYPES is table of REC_TYPE;
type REC_CURSOR is ref cursor;
ref_cursor REC_CURSOR;
recs REC_TYPES;
sql_query VARCHAR2(4000);
begin
sql_query := 'select s.ID, COL1, COL2 from SOURCE_TABLE s, DESTINATION_TABLE d where s.ID = d.ID';
open ref_cursor for sql_query;
fetch ref_cursor bulk collect into recs;
close ref_cursor;
FOR ind IN recs.FIRST .. recs.LAST
loop
update DESTINATION_TABLE
set COL_A = recs(ind).COL1, COL_B = recs(ind).COL2
WHERE ID = recs(ind).ID;
end loop;
commit;
end;
/
Is it possible to use a ref_cursor with ORACLE WITH CLAUSE. For example, I have the following scenario. First I have a procedure which returns a ref_cursor
PROCEDURE p_return_cursor(p_id IN NUMBER, io_cursor OUT t_cursor)
AS
BEGIN
OPEN io_cursor FOR
SELECT col1, col2
FROM Table1 t
WHERE t.id = p_id;
END;
Second, I have another procedure on which I make a call to p_return_cursor:
PROCEDURE p_test(p_cid IN NUMBER)
AS
l_cursor t_cursor;
l_rec Table1%ROWTYPE;
BEGIN
p_return_cursor(p_id => p_cid, io_cursor => l_cursor);
-- CODE GOES HERE
...
Now, my question is, can I make a temp table using the Oracle's WITH CLAUSE using the cursor; something like:
...
WITH data AS (
LOOP
FETCH l_cursor INTO l_rec;
EXIT WHEN l_cursor%NOTFOUND;
SELECT l_rec.col1, l_rec.col2 FROM DUAL;
END LOOP;
CLOSE l_cursor;
)
You cannot do it directly. You can, however, BULK COLLECT your cursor into a PL/SQL table variable and use that in a WITH clause.
Be careful of memory usage if the cursor contains many rows.
Full example:
CREATE TABLE table1 ( col1 NUMBER, col2 NUMBER );
INSERT INTO table1 ( col1, col2 ) SELECT rownum, 100+rownum FROM DUAL CONNECT BY ROWNUM <= 15;
COMMIT;
CREATE OR REPLACE PACKAGE se_example AS
TYPE t_cursor IS REF CURSOR
RETURN table1%ROWTYPE;
TYPE l_rec_tab IS TABLE OF table1%ROWTYPE;
PROCEDURE p_test (p_cid IN NUMBER);
END se_example;
CREATE OR REPLACE PACKAGE BODY se_example AS
-- private
PROCEDURE p_return_cursor (p_id IN NUMBER, io_cursor OUT t_cursor) AS
BEGIN
OPEN io_cursor FOR
SELECT col1,
col2
FROM table1 t;
--WHERE t.id = p_id; -- I didn't put "id" column in my sample table, sorry...
END p_return_cursor;
PROCEDURE p_test (p_cid IN NUMBER) IS
l_cursor t_cursor;
l_tab l_rec_tab;
l_dummy NUMBER;
BEGIN
p_return_cursor (p_id => p_cid, io_cursor => l_cursor);
FETCH l_cursor BULK COLLECT INTO l_tab;
-- *** instead of this
-- WITH data AS (
-- LOOP
-- FETCH l_cursor INTO l_rec;
-- EXIT WHEN l_cursor%NOTFOUND;
-- SELECT l_rec.col1, l_rec.col2 FROM DUAL;
-- END LOOP;
-- CLOSE l_cursor;
-- )
-- '
--
-- *** do this
WITH data AS
( SELECT col1, col2 FROM TABLE(l_tab) )
SELECT sum(col1 * col2) INTO l_dummy
FROM data;
dbms_output.put_line('result is ' || l_dummy);
END p_test;
END se_example;
begin
se_example.p_test(100);
end;
I am new to PL/SQL. Can someone help me to solve the following scenario.
How to get the employee details like emp_id,full_name,department_id,total_salary
if we give Postal code as input parameter to the procedure. I know how to write a procedure to employee details from one table but here we have to use three tables.
Can any one help.
I am also providing the code I have used
Create or Replace Procedure P1(postal_cd varchar2)
as
v_empid number;
v_name varchar2(20);
v_dept_Id number(10);
v_dept_nm varchar2(10);
v_hiredate date;
v_sal number;
begin
select e.emp_id
, e.full_name||' '||e.last_name
, e.department_id
, d.department_name
, e.hiredata
, sum(e.sal) salary
into v_empid
, v_name
, v_dept_id
, v_dept_nm
, v_hiredate
, v_sal
from employees e
inner join departments d
on e.department_id = d.department_id
inner join locations l
on d.location_id = l.location.id
where l.postal_code = postal_cd
;
end;
Kindly let me know if the above query gives me the required result. Thanks in advance.
Since you want to display employee details belonging to a particular postal code the following code should help.
CREATE OR REPLACE PROCEDURE p1(postal_cd VARCHAR2)
AS
v_empid NUMBER;
v_name VARCHAR2(20);
v_dept_Id NUMBER(10);
v_dept_nm VARCHAR2(10);
v_hiredate DATE;
v_sal NUMBER;
CURSOR cur_employee
IS
SELECT e.emp_id,
e.full_name||' '||e.last_name AS emp_full_name,
e.department_id,
d.department_name,
e.hiredata,
e.sal
INTO v_empid,
v_name,
v_dept_id,
v_dept_nm,
v_hiredate,
v_sal
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
INNER JOIN locations l ON d.location_id = l.location_id
WHERE l.postal_code = postal_cd;
BEGIN
FOR c_employee IN cur_employee
LOOP
DBMS_OUTPUT.PUT_LINE(c_employee.emp_full_name || ' ' ||c_employee.department_id||' '||c_employee.department_name||' '||c_employee.hiredata ||' ' || c_employee.sal);
END LOOP;
END;