Function & Cursor HELP ORACLE PL/SQL - database

Trying to get this output:
SELECT E.ID_EMPLEADO ,E.APEMPPAT , E.ID_JEFE
FROM EMPLEADOS E
WHERE E.ID_SUCURSAL=4
CONNECT BY PRIOR E.ID_EMPLEADO = E.ID_JEFE ;
with this function
CREATE OR REPLACE FUNCTION EMPLO_FUN
(V_EMPL_SUC IN EMPLEADOS.ID_SUCURSAL%TYPE)
RETURN VARCHAR2
IS
V_NEMP_HASR EMPLEADOS.ID_EMPLEADO%TYPE;
V_DEP_HASR EMPLEADOS.APEMPPAT%TYPE;
V_JURIS_HASR EMPLEADOS.ID_JEFE%TYPE;
CURSOR C1
IS
SELECT E.ID_EMPLEADO ,E.APEMPPAT, E.ID_JEFE
INTO V_NEMP_HASR, V_DEP_HASR, V_JURIS_HASR
FROM EMPLEADOS E
WHERE E.ID_SUCURSAL=V_EMPL_SUC
CONNECT BY PRIOR E.ID_EMPLEADO = E.ID_JEFE ;
BEGIN
OPEN c1;
FETCH c1 INTO V_NEMP_HASR, V_DEP_HASR, V_JURIS_HASR;
CLOSE c1;
RETURN V_NEMP_HASR;
RETURN V_DEP_HASR;
RETURN V_JURIS_HASR;
END;
SELECT EMPLO_FUN (4) FROM DUAL;
I know that functions mostly return one value serching found that i can use a cursor to get multiples values, but i need help,I think some loop sentence is missing

In your Query you are returning VARCHAR2 which will return only a value. Instead you should return SYS_REFCURSOR
CREATE OR REPLACE FUNCTION EMPLO_FUN (V_EMPL_SUC IN EMPLEADOS.ID_SUCURSAL%TYPE)
RETURN SYS_REFCURSOR
AS
V_MYRESULT SYS_REFCURSOR;
BEGIN
OPEN V_MYRESULT FOR
SELECT E.ID_EMPLEADO ,E.APEMPPAT, E.ID_JEFE
FROM EMPLEADOS E
WHERE E.ID_SUCURSAL=V_EMPL_SUC
CONNECT BY PRIOR E.ID_EMPLEADO = E.ID_JEFE ;
RETURN V_MYRESULT;
END;
Then SELECT EMPLO_FUN (4) FROM DUAL; should give you the expected result.

Related

Taking Previous Months' Date Automatically in PLSQL

Hello I have a procedure and questions about it. This procedure is used for extracting data then inserting them into one table. When I test my code, I have to enter some parameters for executing procedure.
`--this is how I execute the procedure
begin
GPU_DATA_EXTRACTOR(to_date('31/08/2021','DD/MM/YYYY'));
end;`
But what I want to do is that when the billdate parameter is NULL, the procedure should execute last day of the previous month as a parameter automatically. How can I make this change? I am open to any update advices thank you from now.
Updated the script below.
create or replace procedure GPU_DATA_EXTRACTOR_TEST(pid_billdate DATE DEFAULT LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE), -1))) is
c_limit CONSTANT PLS_INTEGER DEFAULT 10000;
CURSOR c1 IS
SELECT DISTINCT intl_prod_id
FROM apld_bill_rt abr,
acct_bill ab
WHERE abr.CHRG_TP = 'INSTALLMENT'
AND abr.TAX_CATG_ID = 'NOTAX'
AND abr.acct_bill_id = ab.acct_bill_id
AND ab.bill_date = pid_billdate;
TYPE prod_ids_t IS TABLE OF apld_bill_rt.intl_prod_id%TYPE INDEX BY PLS_INTEGER;
l_prod_ids prod_ids_t;
begin
execute immediate 'truncate table GPU_INV_TEST';
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO l_prod_ids LIMIT c_limit;
EXIT WHEN l_prod_ids.COUNT = 0;
FORALL indx IN 1 .. l_prod_ids.COUNT
INSERT INTO GPU_INV_TEST
SELECT AB.ACCT_BILL_ID,
AB.BILL_NO,
AB.INV_ID,
AB.BILL_DATE,
ba2.bill_acct_id,
ba1.bill_acct_id parent_bill_acct_id,
AB.DUE_DATE,
PG.CMPG_ID,
ABR.NET_AMT,
AB.DUE_AMT,
P.PROD_NUM,
pds.DST_ID,
ABR.DESCR,
p.intl_prod_id
FROM apld_bill_rt abr,
acct_bill ab,
prod p,
FCBSADM.PROD_DST pds,
bill_acct_prod bap,
bill_acct ba1,
bill_acct ba2,
prod_cmpg pg
WHERE ab.intl_bill_acct_id = ba1.intl_bill_acct_id
AND AB.ACCT_BILL_ID = ABR.ACCT_BILL_ID
AND ba1.intl_bill_acct_id = ba2.parent_bill_acct_id
AND ba2.intl_bill_acct_id = bap.intl_bill_acct_id
AND bap.intl_prod_id = abr.intl_prod_id
AND ABR.CHRG_TP = 'INSTALLMENT'
AND bap.intl_prod_id = pds.intl_prod_id
AND bap.intl_prod_id = p.intl_prod_id
AND p.intl_prod_id = pg.intl_prod_id(+)
AND ABR.intl_prod_id = l_prod_ids(indx)
UNION
SELECT AB.ACCT_BILL_ID,
AB.BILL_NO,
AB.INV_ID,
AB.BILL_DATE,
ba1.bill_acct_id,
ba1.bill_acct_id parent_bill_acct_id,
AB.DUE_DATE,
PG.CMPG_ID,
ABR.NET_AMT,
AB.DUE_AMT,
P.PROD_NUM,
pds.DST_ID,
ABR.DESCR,
p.intl_prod_id
FROM apld_bill_rt abr,
acct_bill ab,
prod p,
FCBSADM.PROD_DST pds,
bill_acct_prod bap,
bill_acct ba1,
prod_cmpg pg
WHERE ab.intl_bill_acct_id = ba1.intl_bill_acct_id
AND AB.ACCT_BILL_ID = ABR.ACCT_BILL_ID
--AND ba1.intl_bill_acct_id = ba2.parent_bill_acct_id
AND ba1.intl_bill_acct_id = bap.intl_bill_acct_id
AND bap.intl_prod_id = abr.intl_prod_id
AND ABR.CHRG_TP = 'INSTALLMENT'
AND bap.intl_prod_id = pds.intl_prod_id
AND bap.intl_prod_id = p.intl_prod_id
AND p.intl_prod_id = pg.intl_prod_id(+)
AND ABR.intl_prod_id = l_prod_ids(indx);
COMMIT;
END LOOP;
CLOSE c1;
end;
You can add a default value for your parameters. Take the following function as an example:
CREATE OR REPLACE FUNCTION sf_showDefault
(
p_in DATE DEFAULT LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE), -1))
)
RETURN DATE
IS
BEGIN
RETURN p_in;
END sf_showDefault;
/
When no parameters are entered it gets a truncated SYSDATE and subtracts one month, then if finds the last day of that month. All the function does is return that data (or the one that you pass in...if you feel like it).
Here is a DBFiddle showing the effect of DEFAULT parameters (LINK)

Oracle function re-write in SQL-Server

I have an Oracle function that needs to be converted to SQL-Server function
This is the Oracle Function:
FUNCTION check_education(in_crs_code IN VARCHAR2)
RETURN BOOLEAN IS
v_bool BOOLEAN := FALSE;
v_dummy VARCHAR2(1);
CURSOR find_education IS
SELECT 'x'
FROM KU_LIBRARY_EDUCATION_EXTLOAN
WHERE UPPER(course_code) = UPPER(in_crs_code) AND in_use = 'Y';
BEGIN
OPEN find_education;
FETCH find_education INTO v_dummy;
IF find_education%FOUND THEN
v_bool := TRUE;
ELSE
v_bool := FALSE;
END IF;
CLOSE find_education;
RETURN (v_bool);
END check_education;
This is what I have written in SQL-Server to replicate Oracle function:
CREATE FUNCTION [dbo].[check_education](#in_crs_code VARCHAR(4000))
RETURNS BIT AS
BEGIN
DECLARE #v_bool BIT = 0;
DECLARE #v_dummy VARCHAR(1);
DECLARE find_education CURSOR LOCAL FOR
SELECT 'x'
FROM [dbo].[KU_LIBRARY_EDUCATION_EXTLOAN]
WHERE UPPER(course_code) = UPPER(#in_crs_code)
AND in_use = 'Y';
OPEN find_education;
FETCH find_education INTO #v_dummy;
IF ##CURSOR_ROWS >1 BEGIN
SET #v_bool = 1;
END
ELSE BEGIN
SET #v_bool = 0;
END
CLOSE find_education;
DEALLOCATE find_education;
RETURN (#v_bool);
END;
I would expect the SQL server function to return 1 if the cursor returns 'x' but i'm getting 0. Anu help will be appreciated.
I would suggest using an inline table valued function instead of a scalar function. To make sure this is an inline table valued function it MUST be a single select statement. This means there can't be loops and other stuff. Fortunately this query does not actually need any loops. A simple count will return the number of rows. And any value other than 0 when converted to a bit will always be 1.
CREATE FUNCTION [dbo].[check_education]
(
#in_crs_code VARCHAR(4000)
) RETURNS table as return
SELECT CourseExists = convert(bit, count(*))
FROM [dbo].[KU_LIBRARY_EDUCATION_EXTLOAN]
WHERE UPPER(course_code) = UPPER(#in_crs_code)
AND in_use = 'Y';
This is a mere EXISTS thing, so we could try
CREATE FUNCTION [dbo].[check_education](#in_crs_code VARCHAR(4000)) RETURNS BIT AS
BEGIN
RETURN EXISTS ( <query> )
END;
But as far as I know, SQL Server doesn't accept this (though I cannot say why not - maybe it's because of their lack of a real boolean; Oracle doesn't accept it, because EXISTS is no keyword in their PL/SQL programming language).
So we'd use IF/ THEN/ ELSE:
CREATE FUNCTION [dbo].[check_education](#in_crs_code VARCHAR(4000)) RETURNS BIT AS
BEGIN
IF EXISTS
(
SELECT 'x'
FROM ku_library_education_extloan
WHERE UPPER(course_code) = UPPER(in_crs_code) AND in_use = 'Y'
)
RETURN 1
ELSE
RETURN 0
END
END;
There may be errors, because I've never coded a stored procedure in T-SQL, but anyway, you get the idea.

Postgresql: Custom Type + Arrays combination

I'm unable to find where the issue is for the below program. the values of the custom type are displaying without any errors when I use RAISE NOTICE statements at the end. When I run the final select statement, the error is Array value must start with "{" or dimension information. Please help me with the select statement on how to call the package/function.
create
or
replace TYPE t_col_foo as object
(
ID NUMBER
, CLUSTERNAME VARCHAR2(300)
, "1200AM" varchar2(10));
create
or
replace TYPE T_COL_R AS TABLE OF t_col_foo;
CREATE OR REPLACE PACKAGE foo_avail_pkg
IS
FUNCTION foo_slots
(
p_ref_data anyarray
)
RETURN t_col_r[];
END foo_avail_pkg;
CREATE OR REPLACE PACKAGE BODY foo_avail_pkg
IS
FUNCTION foo_slots
(
p_ref_data anyarray
)
RETURN t_col_r[]
IS
-- declare
r_target_data t_col_foo:=t_col_foo(null,null,null);
r_target_data_1 t_col_foo;
r_source_data text[];
t_return t_col_tab1;
BEGIN
t_return:=t_col_tab1();
select
array
(
select
unnest( p_ref_data )
)
into r_source_data
;
-- r_target_data = '{}';
for i in coalesce(array_lower(r_source_data,1),0) .. coalesce(array_upper(r_source_data,1),0)
LOOP
r_target_data.ID := substr(r_source_data[i],1,instr(r_source_data[i],',',1,1)-1);
r_target_data.CLUSTERNAME := substr(r_source_data[i],length(r_target_data.ID)+2,(instr(r_source_data[i],',',length(r_target_data.ID)+1,2) - instr(r_source_data[i],',',1,1))-1);
r_target_data."1200AM" := 3;
r_target_data_1 :=row(r_target_data.ID ,r_target_data.CLUSTERNAME,r_target_data."1200AM") :: t_col_foo;
END LOOP;
-- dbms_output.put_line(r_target_data_1);
RETURN r_target_data_1;
end;
END foo_avail_pkg;
This is how I have to call
select * from foo_avail_pkg.foo_SLOTS(array
(
select
ID
||','
||CLUSTER_NAME
||','
||LOB
from
y limit 1
));
And the error is
ERROR: malformed array literal: "(1398,Sanity20feb,3)"
DETAIL: Array value must start with "{" or dimension information.

Netezza while loop syntax

I want to make a statement in netezza so that it waits until a statement is correct before proceeding. Any help would be appreciated - something similar to the below
WHILE (
select count(*) EVENT_DESCRIPTION from TEST_DA_CONTROL.CTRL.C_DBA_MAINTENANCE_AUDIT
where EVENT_DESCRIPTION = 'STARTED' and DATETIME_LOGGED > (select add_months(current_date,0))) = 0
LOOP
wait 5
end loop;
but I don't know the correct syntax.
Best to assign that output to a variable. I seem to recall that getting data out of an execute immediate is a little arduous in nzplsql, but there are convenient variables already available for you to use. Here I'll use ROW_COUNT.
declare
event_descriptions int;
sql varchar;
begin
event_descriptions := 1;
while event_descriptions > 0 loop
--Actual work
sql := '
select * EVENT_DESCRIPTION from TEST_DA_CONTROL.CTRL.C_DBA_MAINTENANCE_AUDIT
where EVENT_DESCRIPTION = ''STARTED'' and DATETIME_LOGGED > (select add_months(current_date,0))) = 0;';
execute immediate sql;
event_descriptions := ROW_COUNT;
end loop;
end;

How to build cursor based on an array

I need to optimize a PL/SQL function that is currently like that:
CREATE OR REPLACE FUNCTION tkt_get_underlying(n_input number)
RETURN t_table_of_number
IS
ret t_table_of_number;
CURSOR c IS SELECT n_number FROM t_table WHERE n_prop_1=n_input OR n_prop_2=n_input OR n_prop_3=n_input;
BEGIN
ret := t_table_of_number();
OPEN c;
FETCH c BULK COLLECT INTO ret;
CLOSE c;
RETURN ret;
END;
I want to be able to give an array as argument, however, I don't know how to build my cursor to take to array. I think I could use the IN statement, but could you help me settle this down please ?
EDIT:
According to solution provided by Justin Cave, it would become:
CREATE OR REPLACE FUNCTION tkt_get_underlying(n_inputs t_table_of_number)
RETURN t_table_of_number
IS
ret t_table_of_number;
CURSOR c IS SELECT n_number FROM t_table WHERE n_prop_1 IN (SELECT column_value FROM TABLE(n_inputs))
OR n_prop_2 IN (SELECT column_value FROM TABLE(n_inputs))
OR n_prop_3 IN (SELECT column_value FROM TABLE(n_inputs));
BEGIN
ret := t_table_of_number();
OPEN c;
FETCH c BULK COLLECT INTO ret;
CLOSE c;
RETURN ret;
END;
However, the multiple SELECT column_value FROM TABLE(n_inputs) slow the entire function. How can I improve that ?
If you want to pass in a collection of n_input values and return the same t_table_of_number collection (i.e. you don't need to know which element of the output array was associated with which element of the input array)
CREATE OR REPLACE FUNCTION tkt_get_underlying(p_inputs t_table_of_number)
RETURN t_table_of_number
IS
ret t_table_of_number;
CURSOR c
IS SELECT n_number
FROM t_table
WHERE n_prop IN (SELECT column_value
FROM TABLE( p_inputs ) );
BEGIN
OPEN c;
FETCH c BULK COLLECT INTO ret;
CLOSE c;
RETURN ret;
END;
This assumes that the number of elements that is going to potentially be inserted into the ret collection is still reasonable to hold in PGA memory simultaneously. Depending on the situation, you may want to transform this into a pipelined table function in order to limit the amount of PGA memory required.
Oracle is getting the cardinality wrong using the nested table, since it will have no idea how many rows are actually there. Try making your function look like:
CREATE OR REPLACE FUNCTION tkt_get_underlying(n_inputs t_table_of_number)
RETURN t_table_of_number
IS
ret t_table_of_number;
CURSOR c IS SELECT n_number FROM t_table WHERE n_prop_1 IN (SELECT /*+ cardinality(ni 1) */ column_value FROM TABLE(n_inputs) ni)
OR n_prop_2 IN (SELECT /*+ cardinality(ni 1) */ column_value FROM TABLE(n_inputs) ni)
OR n_prop_3 IN (SELECT /*+ cardinality(ni 1) */ column_value FROM TABLE(n_inputs) ni);
BEGIN
ret := t_table_of_number();
OPEN c;
FETCH c BULK COLLECT INTO ret;
CLOSE c;
RETURN ret;
END;
Note, if you know how many rows you expect in the nested table, make your cardinality hint accurate. Also, if you put too many rows in the nested table, Oracle could perform sub-optimally because you are making it think there are less rows in the nested table than what it really has.
Thank you for all your help, I finally find THE optimization that fit my needs. Now the query is like:
CREATE OR REPLACE FUNCTION tkt_get_underlying(n_inputs t_table_of_number)
RETURN t_table_of_number
IS
ret t_table_of_number;
CURSOR c IS SELECT t.n_number FROM t_table t, (SELECT column_value /*+cardinality(t_inputs 100) */ c FROM TABLE(n_inputs)) t_inputs
WHERE t_inputs.c = t.n_prop_1
OR t_inputs.c = t.n_prop_2
OR t_inputs.c = t.n_prop_3;
BEGIN
ret := t_table_of_number();
OPEN c;
FETCH c BULK COLLECT INTO ret;
CLOSE c;
RETURN ret;
END;
It does a JOIN that is better than a IN

Resources