create function using one IN and 2 OUT parameters - oracle

I want to create a Oracle function which consists of one input parameters and 2 output parameters. So below is the code I wrote.
CREATE OR REPLACE FUNCTION GET_CMM_REPORT
(
p_span_link_id IN nvarchar2,
p_cmmapproved OUT SYS_REFCURSOR,
p_cmm_tobe_approved OUT SYS_REFCURSOR
)
RETURN VARCHAR2 AS
BEGIN
select ROUTE_APPROVED_BY_CMM AS "CMM APPROVED" from TBL_FIBER_INV_CMPAPPROVED_INFO where
SPAN_LINK_ID= p_span_link_id
and ROUTE_APPROVED_BY_CMM > 0;
select NE_LENGTH
from TBL_FIBER_INV_CMPAPPROVED_INFO where SPAN_LINK_ID= p_span_link_id;
RETURN NULL;
END GET_CMM_REPORT;
Is my function OK as I am new to create oracle function. Please suggest how to implement this.

This should be syntactically valid
CREATE OR REPLACE FUNCTION GET_CMM_REPORT
(
p_span_link_id IN nvarchar2,
p_cmmapproved OUT SYS_REFCURSOR,
p_cmm_tobe_approved OUT SYS_REFCURSOR
)
RETURN VARCHAR2 AS
BEGIN
open p_cmmapproved for
select ROUTE_APPROVED_BY_CMM AS "CMM APPROVED"
from TBL_FIBER_INV_CMPAPPROVED_INFO
where SPAN_LINK_ID= p_span_link_id
and ROUTE_APPROVED_BY_CMM > 0;
open p_cmm_tobe_approved for
select NE_LENGTH
from TBL_FIBER_INV_CMPAPPROVED_INFO
where SPAN_LINK_ID= p_span_link_id;
RETURN NULL;
END GET_CMM_REPORT;
Logically, though, a function that has two out parameters and returns a hard-coded null is rather odd. You'd be better off with a stored procedure instead
CREATE OR REPLACE PROCEDURE GET_CMM_REPORT
(
p_span_link_id IN nvarchar2,
p_cmmapproved OUT SYS_REFCURSOR,
p_cmm_tobe_approved OUT SYS_REFCURSOR
)
AS
BEGIN
open p_cmmapproved for
select ROUTE_APPROVED_BY_CMM AS "CMM APPROVED"
from TBL_FIBER_INV_CMPAPPROVED_INFO
where SPAN_LINK_ID= p_span_link_id
and ROUTE_APPROVED_BY_CMM > 0;
open p_cmm_tobe_approved for
select NE_LENGTH
from TBL_FIBER_INV_CMPAPPROVED_INFO
where SPAN_LINK_ID= p_span_link_id;
END GET_CMM_REPORT;
Or create two separate functions that each return a single cursor.

Related

Executing function embedded SELECT sql query to increase performance

I have the following SQL query that will return the name of a column in a specific table. Let's say it return 'USER_PK' as column name when it runs.
the query:
SELECT max(COLUMN_NAME)
FROM ALL_TAB_COLUMNS
WHERE OWNER= 'DW_01'
AND table_name='D_O_USERS'
AND COLUMN_NAME<>'USER_PK';
Now I would like to run the above query as part of a function but instead of running it and storing the value it returns in a variable (using INTO or attribution like initial_sql: = '...', followed by exec ) I would need to have it run inside one line of code as below (see part in bold)... So far I have been unsuccessful as it is interpreted as a string when using quotes ...
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2, db_table IN VARCHAR2, pk_name IN VARCHAR2, id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( '|| **SELECT max( COLUMN_NAME) FROM ALL_TAB_COLUMNS WHERE OWNER='' || db_schema || '' AND table_name=''||db_table ||'' AND COLUMN_NAME<>'' ||pk_name ||'** ,''SHA512'' ) from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT ;
return result;
end;
Many thanks in advance for your thoughts!
You need to amend you r code like below -
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2,
db_table IN VARCHAR2,
pk_name IN VARCHAR2,
id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( ' || pk_name || ',256 )
from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT;
return result;
end;
/
There are only a few ways to run dynamic SQL in SQL, and they're neither pretty nor fast. The function below uses DBMS_XMLGEN.GETXML to dynamically run a SQL statement.
create or replace function execute_auto(db_schema in varchar2, db_table in varchar2, pk_name in varchar2, id_pk in integer) return varchar2
is
v_column_name varchar2(128);
v_result varchar2(4000);
begin
select standard_hash(to_number(extractValue(xml_results, '/ROWSET/ROW/' || max_column)), 'SHA512') hash_value
into v_result
from
(
--Create a single XML file with the ROWIDs that match the condition.
select max(column_name) max_column, xmltype(dbms_xmlgen.getxml
('
select '||max(column_name)||'
from '||db_schema||'.'||db_table||'
where id = '||id_pk
)) xml_results
from all_tab_columns
where owner = db_schema
and table_name = db_table
and column_name <> pk_name
);
return v_result;
end;
/
For example, let's create this sample table with 100,000 rows:
--drop table test1;
create table test1(id number, a number, b number, constraint pk_test1 primary key(id));
insert into test1
select level, level, level from dual connect by level <= 100000;
commit;
This shows how to use the function;
select execute_auto(user, 'TEST1', 'ID', id) hash
from test1 where id = 1;
HASH
----
A36753F534728ED84A463ECB13750B8E920A7E4D90244258DE77D9800A0F3DAF8CBAD49602E960A2355933C689A23C30377CE10FC4B8E1F197739FF86C791022
In addition to problems with type conversion and SQL injection, the performance is terrible. Selecting all 100,000 rows this way takes 200 seconds on my machine.
select sum(length(execute_auto(user, 'TEST1', 'ID', id))) from test1;
Normally, running everything in a single SELECT statement is a good way to improve performance. But this extreme type of dynamic SQL will never run fast. You probably want to rethink your approach. Instead of trying to optimize the SQL inside a function that is run one-row-at-a-time, try to change the process to process once-per-table

Return Count in Procedure - Oracle

I have this procedure into a package, and I need to return the count of a table, but I don't know how do this. I have this and it doesn't work.
PROCEDURE OBTENER_TOTALCAMPAƑA(TOTAL OUT NUMBER)
IS
row_count number;
BEGIN
SELECT COUNT(*) AS TOTAL_CARTERA INTO row_count FROM CLIENTE
RETURNING row_count INTO TOTAL;
END;
Try this. You don't need to say returning in this case. Returning is typically used in inserts that you need to get the id from after the row is inserted. In your case, just fill the output param with the select ... INTO from ....
PROCEDURE OBTENER_TOTALCAMPAƑA(TOTAL OUT NUMBER)
IS
BEGIN
SELECT COUNT(*) AS TOTAL_CARTERA INTO TOTAL FROM CLIENTE;
END;
Buena suerte
try this. it works for me. create oracle package
create or replace PACKAGE LEVEL_CRUD AS
PROCEDURE INSERT_LEVEL_DATA (
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
P_LEVEL_NAME IN APP_LEVEL_MASTER.LEVEL_NAME%TYPE,
P_CREATED_BY IN APP_LEVEL_MASTER.CREATED_BY%TYPE,
P_CREATE_DATE IN VARCHAR2,
P_UPDATE_DATE IN VARCHAR2
);
PROCEDURE FETCH_LEVEL_DATA (
LEVEL_C OUT SYS_REFCURSOR
);
PROCEDURE UPDATE_LEVEL_DATA (
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
P_LEVEL_NAME IN APP_LEVEL_MASTER.LEVEL_NAME%TYPE,
P_CREATED_BY IN APP_LEVEL_MASTER.CREATED_BY%TYPE,
P_CREATE_DATE IN APP_LEVEL_MASTER.CREATE_DATE%TYPE,
P_UPDATE_DATE IN varchar2,
P_UPDATE_COUNT OUT NUMBER
);
PROCEDURE GET_LEVEL_RECORD_ID_WISE(
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
LEVEL_C OUT SYS_REFCURSOR
);
PROCEDURE DELETE_LEVEL_DATA(
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
DELETE_C OUT NUMBER
);
END LEVEL_CRUD;
create package body where actual procedure is available and sql query will return count and data
create or replace PACKAGE BODY LEVEL_CRUD AS
PROCEDURE INSERT_LEVEL_DATA (
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
P_LEVEL_NAME IN APP_LEVEL_MASTER.LEVEL_NAME%TYPE,
P_CREATED_BY IN APP_LEVEL_MASTER.CREATED_BY%TYPE,
P_CREATE_DATE IN VARCHAR2,
P_UPDATE_DATE IN VARCHAR2
) AS
BEGIN
INSERT INTO APP_LEVEL_MASTER
( LEVEL_ID,LEVEL_NAME,CREATED_BY,CREATE_DATE,UPDATE_DATE,STATUS)
VALUES ( ESC_LEVEL_ID.NEXTVAL,P_LEVEL_NAME,P_CREATED_BY,SYSDATE,SYSDATE,1);
END INSERT_LEVEL_DATA;
PROCEDURE FETCH_LEVEL_DATA (
LEVEL_C OUT SYS_REFCURSOR
) AS
BEGIN
open LEVEL_C FOR
SELECT * FROM APP_LEVEL_MASTER where STATUS = 1;
END FETCH_LEVEL_DATA;
PROCEDURE UPDATE_LEVEL_DATA (
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
P_LEVEL_NAME IN APP_LEVEL_MASTER.LEVEL_NAME%TYPE,
P_CREATED_BY IN APP_LEVEL_MASTER.CREATED_BY%TYPE,
P_CREATE_DATE IN APP_LEVEL_MASTER.CREATE_DATE%TYPE,
P_UPDATE_DATE IN varchar2,
P_UPDATE_COUNT OUT NUMBER
) AS
BEGIN
UPDATE APP_LEVEL_MASTER AUM
SET
AUM.LEVEL_NAME = P_LEVEL_NAME,
AUM.CREATED_BY = P_CREATED_BY,
AUM.CREATE_DATE= TO_DATE(P_CREATE_DATE,'DD/MM/YYYY'),
AUM.UPDATE_DATE=TO_DATE(sysdate)
WHERE AUM.LEVEL_ID = P_LEVEL_ID;
P_UPDATE_COUNT:=SQL%ROWCOUNT;
END UPDATE_LEVEL_DATA;
PROCEDURE GET_LEVEL_RECORD_ID_WISE(
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
LEVEL_C OUT SYS_REFCURSOR
)AS
BEGIN
OPEN LEVEL_C FOR
SELECT * FROM APP_LEVEL_MASTER WHERE LEVEL_ID=P_LEVEL_ID;
END GET_LEVEL_RECORD_ID_WISE;
PROCEDURE DELETE_LEVEL_DATA(
P_LEVEL_ID IN APP_LEVEL_MASTER.LEVEL_ID%TYPE,
DELETE_C OUT NUMBER
)AS
BEGIN
UPDATE APP_LEVEL_MASTER SET STATUS='0' WHERE LEVEL_ID=P_LEVEL_ID;
DELETE_C:=SQL%ROWcOUNT;
END DELETE_LEVEL_DATA;
END LEVEL_CRUD;

'Member of' in Oracle

I am trying to use member of in Oracle.
I am able to use this when table type is of number or any other data type. Below is the code for this:
declare
type t is table of number;
lt t;
begin
select channel_key
bulk collect into lt
from dim_channels;
if 22 member of lt then
dbms_output.put_line('ss');
end if;
end;
How do I use member of when the table is based on a record as in the code below.
declare
type rt is record
(
channel_key number(10),
channel_code varchar2(100)
);
type t is table of rt;
lt t;
lrt rt;
begin
select channel_key, channel_code
bulk collect into lt
from dim_channels;
end;
This won't work with plain local PL/SQL record types. To include more attributes you will need an object type with a MAP or ORDER function:
create or replace type demo_ot as object
( channel_key integer
, channel_code varchar2(30)
, map member function demo_map return varchar2 )
/
create or replace type body demo_ot as
map member function demo_map return varchar2
is
begin
return self.channel_key || '<#>' || self.channel_code;
end demo_map;
end;
/
declare
type demo_t is table of demo_ot; -- You would normally create this globally in SQL
my_set demo_t;
my_object demo_ot;
begin
select demo_ot(ckey, ccode)
bulk collect into my_set
from ( select 1 as ckey, 'One' as ccode from dual
union all
select 2 as ckey, 'Two' as ccode from dual );
my_object := demo_ot(2, 'Two');
if my_object member of my_set then
dbms_output.put_line('Member found');
else
dbms_output.put_line('Member not found');
end if;
end;
/
I created below code to test it
create or replace PROCEDURE P_MEMBER_OF_TEST(p_fname IN VARCHAR2,
p_lname in varchar2)
AS
type type_rec is record
(
first_name employees.first_name%type,
last_name employees.last_name%type
);
TYPE T_TAB_TYPE IS TABLE OF type_rec;
T_TAB T_TAB_TYPE;
t_rec type_rec;
i int;
BEGIN
t_rec.first_name := p_fname;
t_rec.last_name := p_lname;
SELECT FIRST_NAME,last_name bulk collect INTO T_TAB FROM
EMPLOYEES;
dbms_output.put_line(t_rec.first_name || ',' || t_rec.last_name);
IF t_rec MEMBER OF T_TAB THEN
DBMS_OUTPUT.PUT_LINE ('YES');
ELSE
DBMS_OUTPUT.PUT_LINE('NO');
END IF;
END;
It compiled with no issues however when i execute it i get error that my connection has been reset , when i comment the if-else-end if block . it gets executed. Can you also suggest what is the problem in code #William Robertson

Calling SP with sys_refcursor as out parameter inside another procedure

I have an SP
create or replace PROCEDURE ALTERNATE_NAME_LOOKUP
( P_NAME IN VARCHAR2,
P_TYPE IN VARCHAR2, retCursor OUT SYS_REFCURSOR
)
I didn't paste the rest of its body; The above procedure works fine on its own (with the body of course)
Now I want to call it from another stored procedure, and I want to traverse over the refcursor.
What I am doing is declaring an_last_cur SYS_REFCURSOR; and calling ALTERNATE_NAME_LOOKUP procedure as ALTERNATE_NAME_LOOKUP(p_req.LASTNAMEEXP,c_LAST, an_last_cur); It compiles.
but when I add following block -
ALTERNATE_NAME_LOOKUP('Roman Reigns','LAST',an_last_cur);
For alt in an_last_cur
Loop
DBMS_OUTPUT.PUT_LINE('ok');
end loop;
It gives compilation error -
PLS-00221: 'AN_LAST_CUR' is not a procedure or is undefined
What am I doing wrong?
create or replace procedure alternate_name_lookup
( p_name in varchar2, p_type in varchar2, retcursor out sys_refcursor )
as
begin
open retcursor for select * from user_objects ;
end;
set serveroutput on
declare
an_last_cur sys_refcursor;
type my_objects is table of user_objects%rowtype;
objects my_objects;
begin
alternate_name_lookup('Roman Reigns','LAST',an_last_cur);
fetch an_last_cur bulk collect into objects;
dbms_output.put_line(objects.count);
for indx in 1 .. objects.count
loop
dbms_output.put_line(objects(indx).object_name);
end loop;
close an_last_cur;
end;
Try this one. Hope this helps. I dont have workspace with me so pardon
syntax erro r if any.
CREATE OR REPLACE PROCEDURE test_ref_prc
( p_ref_out OUT sys_refcursor)
AS
BEGIN
OPEN p_ref_out FOR
SELECT LEVEL FROM DUAL CONNECT BY LEVEL < 10;
END;
CREATE OR REPLACE PROCEDURE test_ref2
AS
refc sys_refcursor;
num_ntt NUMBER_NTT;
BEGIN
test_ref_prc(refc);
FETCH refc BULK COLLECT INTO num_ntt;
FOR I IN num_ntt.FIRST..num_ntt.LAST LOOP
dbms_output.put_line(num_ntt(i));
END LOOP;
END;
exec test_ref2;

Can't retrieve data using dynamic SQL from the function that returns the table

My types:
TYPE T_rowBalanceListForClient IS RECORD
(
RowCode Asset.RowCode%TYPE,
RowName Asset.RowName%TYPE
);
TYPE T_tableBalanceListForClient IS TABLE OF T_rowBalanceListForClient;
My function:
FUNCTION F_BalanceListForClient
(
p_ClientId Client.ClientId%TYPE
)
RETURN T_tableBalanceListForClient PIPELINED
AS
CURSOR CUR_TABLE IS
SELECT
RowCode,
RowName
FROM Asset;
BEGIN
FOR CUR_REC IN CUR_TABLE LOOP
PIPE ROW(CUR_REC);
END LOOP;
END;
Part of my stored procedure:
sql_statement := ' SELECT * FROM TABLE(:1)';
OPEN c_Result FOR sql_statement USING F_BalanceListForClient(11);
While building the package I reseive the Oracle error:
PLS-00457: expressions have to be of SQL types
In the common stored procedures calls like this are built and operate well (not dynamics):
PROCEDURE GET_BALANCE_STANDARD_LIST
(
c_Result OUT SYS_REFCURSOR,
p_ClientId Client.ClientId%TYPE DEFAULT NULL
)
AS
BEGIN
OPEN c_Result FOR
SELECT RowName FROM TABLE(F_BalanceListForClient(p_ClientId));
END;
Appreciate any help.
Thanks.
CREATE OR REPLACE TYPE T_rowBalanceListForClient IS OBJECT
(
RowCode NUMBER,
RowName VARCHAR2(200)
);
CREATE OR REPLACE TYPE T_tableBalanceListForClient AS TABLE OF T_rowBalanceListForClient;
/
CREATE OR REPLACE FUNCTION F_BalanceListForClient
(
p_ClientId NUMBER
)
RETURN T_tableBalanceListForClient PIPELINED
AS
CURSOR CUR_TABLE IS
SELECT
RowCode,
RowName
FROM Assets
; --put a filter of the p_clientId
BEGIN
FOR CUR_REC IN CUR_TABLE
LOOP
pipe row (T_rowBalanceListForClient (CUR_REC.RowCode, CUR_REC.RowName));
END LOOP;
RETURN;
END;
/
CREATE OR REPLACE PROCEDURE GET_BALANCE_STANDARD_LIST
(
c_Result OUT SYS_REFCURSOR,
p_ClientId NUMBER DEFAULT NULL
)
AS
sql_statement varchar2(200);
BEGIN
sql_statement := ' SELECT * FROM TABLE(F_BalanceListForClient(:1))';
OPEN c_Result FOR sql_statement USING p_ClientId;
END;
/
BEGIN
GET_BALANCE_STANDARD_LIST(:cur ,11);
END;
/

Resources