Oracle return table from function in package - sql

How can I return a Table from a sql package function without using a cursor ?

I usually like to implement the pipelined function with all objects inside the package.
This is the result query:
select *
from table(pkg_example_pipeline.F_PIPELINE_EXEMPLE(0));
The package:
create or replace PACKAGE pkg_example_pipeline IS
-- *********** TYPES
TYPE TYPE_EXEMPLE is record (
id NUMBER(10,0),
name VARCHAR2(100),
date_test DATE
);
TYPE v_table_exemple is table of TYPE_EXEMPLE;
-- ***********
-- *********** PIPELINE FUNCTIONS
FUNCTION F_PIPELINE_EXEMPLE (pId NUMBER)
RETURN v_table_exemple pipelined;
END; --PACKAGE SPEC
/
create or replace PACKAGE BODY pkg_example_pipeline
IS
-- *********** CURSORs
/**
* Just if you need
**/
CURSOR cr_exemple
IS
SELECT 0 id, 'name' name, sysdate date_test
from dual;
-- ***********
-- *********** PIPELINE FUNCTIONS
--POSICAO DIARIA
FUNCTION F_PIPELINE_EXEMPLE (pId NUMBER)
RETURN v_table_exemple pipelined
IS
row_exemple TYPE_EXEMPLE;
BEGIN
FOR reg_exemple IN (SELECT 0 id, 'name' name, sysdate date_test from dual) LOOP
BEGIN
row_exemple.id := reg_exemple.id;
row_exemple.name := reg_exemple.name;
row_exemple.date_test := reg_exemple.date_test;
pipe row (row_exemple);
EXCEPTION
WHEN others THEN
dbms_output.put_line('error: ' || sqlerrm);
row_exemple.name := 'error: ' || sqlerrm;
pipe row (row_exemple);
END;
END LOOP;
EXCEPTION
WHEN others THEN
ROLLBACK;
END;
-- ***********
END; --PKG BOBY

Related

Passing Multiple Values To PL/SQL Input

I have an Oracle package which contains a function. This function has 3 inputs. I need to pass multiple values to each input. I could automate a process which runs it multiple times with each combination of variables but I would like to only make one call to the database.
Simplified Code
declare
ln_ret number;
begin
ln_ret := dbo.pkg_rpa.mis_run_script (
'%2020%',
'111','222','333','444',
'1234','2345','6192','1204'
);
dbms_output.put_line('ln_ret=' || t.t (ln_ret));
end;
CREATE OR REPLACE
package dbo.pkg_rpa IS
function mis_run_script (
p_input_dt in varchar2,
p_hospital_id in varchar2,
p_procedure_code in varchar2) RETURN number;
end PKG_RPA;
/
CREATE OR REPLACE
PACKAGE BODY dbo.pkg_rpa IS
function mis_run_claim_assessment_script (
p_input_dt in varchar2,
p_hospital_id in varchar2,
p_procedure_code in varchar2
)
Begin
for i in (select table_name from user_tables where lower(table_name) = 'temp_rpa') loop
execute immediate 'drop table temp_rpa';
end loop;
execute immediate ' create table temp_rpa as select distinct ci.claim_id, count(ci.receipt_id) as count_receipts,
sum(ci.billed_amount) as total_billed_amount, count(*) as claim_items
from claim_item ci left join claim_header ch on ch.claim_id = ci.claim_id
left join cd_hos ho on ho.hospital_id = ci.hospital_id
left join claim_type_header cl on cl.claim_id = ci.claim_id
where cl.claim_status is null and ch.deleted_flag is null
and ch.input_dt like p_input_dt
and ci.hospital_id in (p_hospital_id)
and (ci.claim_id, NVL(ci.claim_item_id,0)) in (select claim_id, NVL(claim_item_id,0) from cd_roc_claim_item
where procedure_code in (p_procedure_code))
and (ci.claim_id, NVL(ci.claim_item_id,0)) not in (select claim_id, NVL(claim_item_id,0) from cd_roc_claim_item
where procedure_code not in (p_procedure_code))
group by ci.claim_id
having sum(case when ci.service_type_id is null then 1 end) = 1)';
End;
end mis_run_script;
end PKG_RPA;
/
Pass it with quoted string (Q'<delimeter><your_actual_string><delimeter>') as follows:
begin
ln_ret := dbo.pkg_rpa.mis_run_script (
'%2020%',
Q'#'111','222','333','444'#',
Q'#'1234','2345','6192','1204'#'
);
dbms_output.put_line('ln_ret=' || t.t (ln_ret));
end;
What you could do is using an associative array as input type. Instead of varchar2, use dbms_sql.varchar2a as date type for the 2nd and 3rd arguments.
But if the arguments are related to each other, let's say
p_hospital_id '111' belongs to procedure code '1234'
p_hospital_id '222' belongs to procedure code '2345'
etc.
I think you would want to create a custom record type, create a table type of the record type and use that as an parameter.
Your arguments become p_hospital_ids in dbms_sql.varchar2a in both the package specification and the package body.
In you code, you would have to loop over it and instead of dropping the table and recreate it each time, you drop it once at the start and add data within the loop;
truncate table; --alternative drop and create
for i in 1 .. p_hospital_ids.count loop
insert into temp_rpa
select <columns>
from claim_item ci
......
and ci.hospital_id = p_hospital_ids[i]
end loop
You may want to refer to the below example which is taken from Oracle Website. Hope it helps.
CREATE OR REPLACE TYPE nt_type IS TABLE OF NUMBER;
/
CREATE OR REPLACE PROCEDURE print_nt (nt nt_type) AUTHID DEFINER IS
i NUMBER;
BEGIN
i := nt.FIRST;
IF i IS NULL THEN
DBMS_OUTPUT.PUT_LINE('nt is empty');
ELSE
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT('nt.(' || i || ') = ');
DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(nt(i)), 'NULL'));
i := nt.NEXT(i);
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE('---');
END print_nt;
/
DECLARE
nt nt_type := nt_type(); -- nested table variable initialized to empty
BEGIN
print_nt(nt);
nt := nt_type(90, 9, 29, 58);
print_nt(nt);
END;
/
You don't need dynamic SQL at all.
CREATE OR REPLACE TYPE NUMBER_TABLE_TYPE AS TABLE OF NUMBER;
CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(1000);
function mis_run_claim_assessment_script (
p_input_dt in varchar2,
p_hospital_id in NUMBER_TABLE_TYPE, -- why on earth do you put numbers as strings?
p_procedure_code in VARCHAR_TABLE_TYPE
) RETURN ??? AS
INSERT INTO temp_rpa (...)
SELECT ...
FROM ...
WHERE ch.input_dt like p_input_dt
AND ci.hospital_id MEMBER OF p_hospital_id
AND procedure_code MEMBER OF p_procedure_code ;
RETURN ???
END;
ln_ret := mis_run_claim_assessment_script(
'%2020%',
NUMBER_TABLE_TYPE(111, 222, 333, 444),
VARCHAR_TABLE_TYPE('not','clear','in','your','question')
);

PLS-00103: Encountered the symbol "CREATE" for package

I have to execute a SQL file (I do not have much idea of SQL, I just have to execute it) but there is an error I do not understand:
Errors: check compiler log
Errors for PACKAGE USER1.TST_MAI_FILTER:
LINE/COL ERROR
29/1 PLS-00103: Encountered the symbol "CREATE"
This is the PACKAGE USER1.TST_MAI_FILTER:
CREATE OR REPLACE PACKAGE TST_MAI_FILTER IS
FUNCTION TRADE_TYPE
RETURN NUMBER;
FUNCTION ORDER_TYPE
RETURN NUMBER;
FUNCTION MRKT_EVT_TYPE
RETURN NUMBER;
FUNCTION DAILY_DATA_TYPE
RETURN NUMBER;
PROCEDURE log_mai_filter(p_run_id NUMBER, p_config_id NUMBER);
PROCEDURE is_valid_filter
(p_mai_class_id IN NUMBER,
p_type_id IN NUMBER,
v_where CLOB);
END TST_MAI_FILTER;
/
I am using Oracle SQL Developer (Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production).
EDIT: This is line 29:
CREATE SEQUENCE S_TRD START WITH 1 INCREMENT BY 1 MINVALUE 1 CACHE 20 NOORDER;
and this is the rest of the package but it does not get until there since the developer stops after this error:
CREATE OR REPLACE PACKAGE BODY TST_MAI_FILTER IS
FUNCTION TRADE_TYPE
RETURN NUMBER
IS
BEGIN
RETURN 1;
END;
FUNCTION ORDER_TYPE
RETURN NUMBER
IS
BEGIN
RETURN 2;
END;
FUNCTION MRKT_EVT_TYPE
RETURN NUMBER
IS
BEGIN
RETURN 3;
END;
FUNCTION DAILY_DATA_TYPE
RETURN NUMBER
IS
BEGIN
RETURN 4;
END;
PROCEDURE log_mai_filter(p_run_id NUMBER, p_config_id NUMBER)
IS
v_mesg VARCHAR2(4000 BYTE) := NULL;
BEGIN
INSERT INTO mai_filter_log (run_id, mai_query_id, sql_query, config_id, type_id, mai_class_id)
SELECT p_run_id, mai_query_id, sql_query, config_id, type_id, mai_class_id
FROM mai_filter
WHERE config_id = p_config_id;
LOG_INFO(p_run_id, 'I', 'RUN_LOG: Log mai_filter: ' || SQL%ROWCOUNT );
EXCEPTION
WHEN OTHERS THEN
v_mesg := 'An error has occured when executing log_mai_filter - ' || SQLCODE || ' -ERROR- ' || SQLERRM;
raise_application_error(-20085, v_mesg);
LOG_INFO(p_run_id, 'E', v_mesg);
END;
PROCEDURE is_valid_filter
(p_mai_class_id IN NUMBER,
p_type_id IN NUMBER,
v_where CLOB) IS
v_tbl VARCHAR2(400 BYTE) := NULL;
BEGIN
SELECT type_name INTO v_tbl
FROM mai_filter_type
WHERE type_id = p_type_id;
EXECUTE IMMEDIATE 'SELECT run_id FROM ' || v_tbl || ' WHERE run_id = 0 AND '|| v_where;
END;
END TST_MAI_FILTER;
/

Invalid Data Type error while casting PL/SQL Array to Table

I have a PL/SQL stored proc which needs to return a RefCursor type object as output parameter.
PROCEDURE usp_appnt_stts_driver_wraper2
( in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec1 OUT SYS_REFCURSOR)
At the end of the SP, I am able to return Hard Coded values to my front end by using a Select Statement.
OPEN out_response_rec1 FOR
SELECT 'data1', 'data2', 'data3', 'data 4' FROM DUAL;
This works fine. But I need to send the data which I am getting from an Array.
The Array is populated like this,
FOR g_index IN g_slsnetoutbndarr.FIRST..g_slsnetoutbndarr.LAST
LOOP
out_response_rec.EXTEND;
IF g_SlsnetOutbndArr(g_index).Rectypdesc IS NOT NULL THEN
out_response_rec(g_index).Rectypdesc := g_SlsnetOutbndArr(g_index).Rectypdesc ;
out_response_rec(g_index).Recdetltcode := g_SlsnetOutbndArr(g_index).Recdetltcode;
out_response_rec(g_index).RecDetlDesc := g_SlsnetOutbndArr(g_index).RecDetlDesc ;
END IF;
END LOOP;
So at the end of this code, the Array Object out_response_rec has all the values I need.
But How do I transfer these values in the RefCursor output parameter?
Update 1
I have tried to create a new data type in the Package specification.
TYPE SlsnetOutbndRec IS RECORD(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130));
TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
Finally I have tried to Cast the Array to table in my SP as
OPEN out_response_rec_result FOR
SELECT * FROM TABLE (Cast(out_response_rec AS SlsnetOutbndTabArr));
But this is throwing an Invalid Data Type error. The SP does not recognize the new data types I created.
So at the end of this code, the Array Object out_response_rec has all
the values I need.
But How do I transfer these values in the RefCursor output parameter?
As I could understand, you wanted to use a SYS_REFCUSOR to get the output of a Query plus the values that are in collection which is being populated by another Procedure. I have come up with a solution for your requirement see below inline explaination:
--Create a Object in Place of Record since we cannot use a PLSQL scope compnenet in SQL scope in Oracle 11g and below
CREATE OR REPLACE TYPE SlsnetOutbndRec IS OBJECT
(
Rectypdesc VARCHAR2 (30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2 (130)
);
--Create a table type of the Object
CREATE OR REPLACE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
/
--Procedure
CREATE OR REPLACE PROCEDURE combined_rslt (var OUT SYS_REFCURSOR)
AS
v_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
l_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
BEGIN
--Populating the collection
v_var.EXTEND;
v_var (1) := SlsnetOutbndRec ('ABC', 1, 'A');
OPEN VAR FOR
SELECT 'CDE', 2, 'B' FROM DUAL
UNION ALL -- Combining the result of collection with the result of query
SELECT *
FROM TABLE (v_var) t;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;
Execution :
DECLARE
x SYS_REFCURSOR;
a VARCHAR2 (30);
b NUMBER;
c VARCHAR2 (130);
BEGIN
combined_rslt (var => x);
LOOP
FETCH x INTO a, b, c;
EXIT WHEN x%NOTFOUND;
DBMS_OUTPUT.put_line (a || ' ' || b || ' ' || c);
END LOOP;
END;
Results:
SQL> /
CDE 2 B
ABC 1 A
PL/SQL procedure successfully completed.
You need to create the types in the SQL scope (not the PL/SQL scope) if you want to use them in SQL statements (in versions prior to Oracle 12):
CREATE TYPE SlsnetOutbndRec IS OBJECT(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130)
)
/
CREATE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec
/
Then you can use it in your procedure (assuming your 3rd party data type is a collection or a VARRAY):
PROCEDURE usp_appnt_stts_driver_wraper2(
in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec_result OUT SYS_REFCURSOR
)
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
BEGIN
FOR i IN 1 .. g_slsnetoutbndarr.COUNT LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
END LOOP;
OPEN out_response_rec_result FOR
SELECT *
FROM TABLE( out_response_rec );
END;
If the 3rd party data type is an associative array then:
...
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
i ThirdPartyIndexType := g_slsnetoutbndarr.FIRST;
BEGIN
WHILE i IS NOT NULL LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
i := g_slsnetoutbndarr.NEXT(i);
END LOOP;
...

Get Tables Name Through Plsql Query

I need a procedure that input user_name and password then get to me the exist tables in that schema with the create script for each table?
This is what I tried, but is wrong:
CREATE OR REPLACE procedure TABLE_INFO(P_USER_NAME IN VARCHAR2,P_PASSWORD IN VARCHAR,P_TABLE_NAME OUT VARCHAR2,P_SCRIPT OUT VARCHAR2)
IS
chk_username all_users.username%type;
CURSOR C IS SELECT table_name FROM USER_TABLES;
t_tablename user_tables.table_name%type;
BEGIN
SELECT username into chk_username from all_users
where chk_username=p_user_name;
open c;
loop
fetch c into t_tablename;
exit when c% notfound;
end loop;
exception when no_data_found then
dbms_output.put_line('Wrong Username Or Password');
close c;
end;
/
Try this code. Let me know if this helps.
CREATE OR REPLACE PROCEDURE TABLE_INFO(
P_USER_NAME IN VARCHAR2,
-- P_PASSWORD IN VARCHAR, --Not required
P_TABLE_NAME OUT sys_refcursor)
-- P_SCRIPT OUT VARCHAR2) --Not required
AS
BEGIN
-----------------------For DDL script use dbms_metadata.get_ddl-------------------------
OPEN P_TABLE_NAME FOR
SELECT OWNER,TABLE_NAME,dbms_metadata.get_ddl('TABLE',TABLE_NAME) "table DDL"
FROM all_tables WHERE upper(OWNER) = upper(P_USER_NAME);
END;
--------------------To EXECUTE this proc---------------------------------
var usr VARCHAR2(100);
VAR p_lst refcursor;
exec TABLE_INFO(<username>,:p_lst);
------------------------------------------------------------------------
Check this out. This is a function returning all the values as per requirement.
-- Obj Creation
CREATE OR REPLACE TYPE my_obj
IS
OBJECT
(
TAB_NAME VARCHAR2(100),
DDL_S VARCHAR2(32676) );
-- Table type Creation
CREATE OR REPLACE TYPE my_tab
IS
TABLE OF my_obj;
--Function Creation---
CREATE OR REPLACE FUNCTION TABLE_INFO_FUN(
P_USER_NAME IN VARCHAR2 )
RETURN my_tab
AS
my_otpt my_tab;
BEGIN
-----------------------For DDL script use dbms_metadata.get_ddl-------------------------
SELECT CAST( multiset
(SELECT my_obj(TABLE_NAME,dbms_metadata.get_ddl('TABLE',TABLE_NAME))
FROM all_tables
WHERE upper(OWNER) = upper(P_USER_NAME)
) AS my_tab )
INTO my_otpt
FROM DUAL;
RETURN my_otpt;
END;
-- Using Function to Select Data -------------
SELECT * FROM TABLE(TABLE_INFO_FUN(<USERNAME>));

Oracle select query error inside procedure [duplicate]

This question already has answers here:
Oracle Procedure error (PLS-00428)
(5 answers)
Closed 8 years ago.
I'm getting error while I'm running select query inside procedure. Error print as follow:
[Error] PLS-00428 (24: 9): PLS-00428: an INTO clause is expected in this SELECT statement
CREATE OR REPLACE PACKAGE BODY PACK_EMP
IS
PROCEDURE find_employee(
P_ID NUMBER,
P_ERR OUT VARCHAR2
)
IS
BEGIN
IF P_ID IS NULL THEN
SELECT * FROM EMPLOYEE WHERE ID = P_ID
ELSE
P_ERR := 'An error occured on database!!';
END IF;
EXCEPTION
WHEN OTHERS THEN
P_ERR := SQLERRM;
END;
END;
CREATE OR REPLACE PACKAGE BODY PACK_EMP
IS
PROCEDURE find_employee(
P_ID NUMBER,
P_ERR OUT VARCHAR2,
V_VAL OUT VARCHAR
)
IS
BEGIN
IF P_ID IS NULL THEN
SELECT FIRTSNAME ||' '||LASTNAME INTO V_VAL FROM EMPLOYEE WHERE ID = P_ID
ELSE
P_ERR := 'An error occured on database!!';
END IF;
EXCEPTION
WHEN OTHERS THEN
P_ERR := SQLERRM;
END;
END;
Use a rowtype. Also I think you meant to say "IF P_ID IS NOT NULL THEN..." because if it is null then your select will never work.
CREATE OR REPLACE PACKAGE BODY PACK_EMP
IS
PROCEDURE find_employee(
P_ID NUMBER,
P_ERR OUT VARCHAR2
)
IS
empRec employee%rowtype; /* ADD THIS LINE */
BEGIN
IF P_ID IS NOT NULL THEN
SELECT * into empRec FROM EMPLOYEE WHERE ID = P_ID;
--now you can reference the columns like this: empRec.id
ELSE
P_ERR := 'An error occured on database!!';
END IF;
EXCEPTION
WHEN OTHERS THEN
P_ERR := SQLERRM;
END;
END;