Function with table parameters Oracle - sql

I have the below function. I would like to make it parametrisable. I would want to give as input parameters table_t5 and dunnv3_t6. Would this be possible? I know how to define functions with strings, numbers... as parameters but I'm struggling to pass tables as input parameters.
CREATE OR REPLACE FUNCTION dunnv3_ec_assign RETURN t5_table
PIPELINED
IS
TYPE t6_type IS
TABLE OF dunnv3_t6%rowtype;
t6_data t6_type;
v_cc table_t5.campaign_code%TYPE;
BEGIN
-- https://stackoverflow.com/a/67398434/1509264
-- License: CC BY-SA 4.0
FOR cur IN (
logic which orders the input table for cursor processing
) LOOP
IF v_cc IS NULL OR v_cc <> cur.cc THEN
v_cc := cur.cc;
SELECT
*
BULK COLLECT
INTO t6_data
FROM
dunnv3_t6
WHERE
cc = cur.cc;
END IF;
DECLARE
a_freqs int_list := int_list();
cum_freq INT := 0;
taken string_list := split_string(cur.an, ', ');
idx INT;
c dunnv3_t6.a%TYPE;
BEGIN
a_freqs.extend(t6_data.count);
FOR i IN 1..t6_data.count LOOP IF ( t6_data(i).a = cur.ay AND t6_data(i).c > 0 ) OR ( cur.ay IS NULL AND t6_data(i).a
NOT MEMBER OF taken AND t6_data(i).c > 0 ) THEN
a_freqs(i) := cum_freq + t6_data(i).c;
cum_freq := cum_freq + t6_data(i).c;
ELSE
a_freqs(i) := cum_freq;
END IF;
END LOOP;
IF cum_freq > 0 THEN
idx := floor(dbms_random.value(0, cum_freq));
FOR i IN 1..t6_data.count LOOP IF idx < a_freqs(i) THEN
c := t6_data(i).a;
t6_data(i).c := t6_data(i).c - 1;
EXIT;
END IF;
END LOOP;
END IF;
PIPE ROW ( t5_data(cur.vk, cur.ay, cur.an, cur.cc, cur.r,
c) );
END;
END LOOP;
END;
CREATE TYPE t5_data AS OBJECT (
vk NVARCHAR2(12),
ay CHAR(10),
an VARCHAR2(4000),
cc VARCHAR2(5),
r NUMBER,
c CHAR(10)
);
CREATE TYPE t5_table IS
TABLE OF t5_data;
Oracle version 12.1. Kindly let me know if additional info required.
EDIT, I would also need the 'logic which logic which orders the input table for cursor processing to be passed as a parameter.

Related

Adapt function to procedure

I have the below function and I would like to turn it into a procedure. What would be the changes I need to accomplish it? I would like to turn it into a procedure because it's kind of a one purpose use function, and I would prefer to have it inside my main package along with my other procedures.
I would like to, instead of returning the table, creating it inside the procedure itself.
CREATE OR REPLACE FUNCTION pick_values RETURN t1_prueba_table
PIPELINED
IS
TYPE t2_type IS
TABLE OF t2%rowtype;
t2_data t2_type;
v_pc t1.pc%TYPE;
BEGIN
-- https://stackoverflow.com/a/67516191/1509264
-- License: CC BY-SA 4.0
FOR cur IN (
SELECT *
FROM t1
ORDER BY pc, r
) LOOP
IF v_pc IS NULL OR v_pc <> cur.pc THEN
v_pc := cur.pc;
SELECT *
BULK COLLECT INTO t2_data
FROM t2
WHERE pc = cur.pc;
END IF;
DECLARE
a_freqs int_list := int_list();
cum_freq INT := 0;
taken string_list := split_string(cur.an, ', ');
idx INT;
c t2.a%TYPE;
BEGIN
a_freqs.extend(t2_data.count);
FOR i IN 1..t2_data.count LOOP
IF t2_data(i).a = cur.ao
AND t2_data(i).c > 0
THEN
-- If there is an "ao" value and it has capacity then assign it to "c"
c := t2_data(i).a;
-- Decrement the appropriate "t2_data" row to show it has been used.
t2_data(i).c := t2_data(i).c - 1;
-- Set the "cum_freq" to 0 so the loop where values are randomly assigned is skipped.
cum_freq := 0;
-- Exit the loop
EXIT;
ELSIF (
t2_data(i).a = cur.ay
AND t2_data(i).c > 0
) OR (
cur.ay IS NULL
AND t2_data(i).a NOT MEMBER OF taken
AND t2_data(i).c > 0
)
THEN
a_freqs(i) := cum_freq + t2_data(i).c;
cum_freq := cum_freq + t2_data(i).c;
ELSE
a_freqs(i) := cum_freq;
END IF;
END LOOP;
IF cum_freq > 0 THEN
idx := floor(dbms_random.value(0, cum_freq));
FOR i IN 1..t2_data.count LOOP
IF idx < a_freqs(i) THEN
c := t2_data(i).a;
t2_data(i).c := t2_data(i).c - 1;
EXIT;
END IF;
END LOOP;
END IF;
PIPE ROW (
t1_prueba_data(cur.pc, cur.vk, cur.ay, cur.ao, cur.an, cur.r, c)
);
END;
END LOOP;
END;
Kindly let me know if any additional information needed.
A package can contain both procedures and functions.
There is no need to convert this from a function to a procedure to include it in a package.
eg
create or replace package test AS
PROCEDURE test_procedure (in_test in varchar2(100));
FUNCTION assessment_newid_f RETURN assessments.assessment_id%TYPE;
END;
create or replace package body test AS
PROCEDURE test_procedure (in_test in varchar2(100)) IS
BEGIN
...
END;
FUNCTION assessment_newid_f RETURN assessments.assessment_id%TYPE IS
BEGIN
...
RETURN ...;
END;
END;

Generate dynamic SQL query with multiple filter option(from application)added in single CLOB - Oracle

There is application in which filter option includes start date, end date, postcode, etc where user can select multiple option from single filter. Ex: User can select 5 different start date, end date and 3 different postcode or can select 1 start date, end date and no postcode. There are 7 other filters and each filter user can select multiple options. (Consider E-Commerce website like Amazon. If you want to buy mobile, you use filter for camera range like 16MP,32MP,48MP and company like 'Samsung','Motorola', 'Nokia' and similarly for processor and screen size as well. So as a developer you want to store all the value in CLOB and give the result)
Input will come something like
<date>
<start_date>01/01/2019</start_date>
<end_date>31/01/2019</end_date>
<start_date>01/03/2019</start_date>
<end_date>31/03/2019</end_date>
<start_date>01/05/2019</start_date>
<end_date>31/05/2019</end_date>
</date>
<pc>
<postcode>56012</postcode>
<postcode>56000</postcode>
<postcode>56234</postcode>
</pc>
Now my query should look something like
select col1,col2,col3 from table_name where between start_date and end_date and between start_date and end_date... and postcode like '56012' and postcode like '56000'
Result set retruned should again be sent in clob.
Is there any way out to solve this problem?
create or replace procedure get_data(p_filter IN clob, p_result clob)
is
v_query clob;
V_condition clob;
v_lp_cnt integer;
v_result clob;
v_where_clause clob;
begin
--Considering output will be returned in XML
v_query := 'SELECT XMLELEMENT (
"data", XMLAGG
(XMLELEMENT
(
"whole_data",
XMLELEMENT ("final_date", final_date),
......... from table_name where col2='xyz'';
--Considering that filter values will be less than 1000
--date
V_condition := NULL;
v_lp_cnt := 0;
FOR crec IN(SELECT t.start_date, t.end_date
FROM XMLTABLE('/Filter/final_date/Date'
PASSING xmltype(p_filter)
COLUMNS
start_date VARCHAR2(240) PATH 'Start_Date',
end_date VARCHAR2(240) PATH 'End_Date')t
WHERE 1 = 1
)
loop
v_lp_cnt := v_lp_cnt +1;
IF v_lp_cnt =1
THEN
v_condition := ' AND (ext.final_date between to_date('''|| crec.start_date ||''',''dd/mm/yyyy'') and to_date('''|| crec.end_date ||''',''dd/mm/yyyy'')';
v_where_clause := v_where_clause||v_condition;
v_condition:= null;
ELSIF (v_lp_cnt >1 and v_lp_cnt <1000)
THEN
v_condition := ' OR ext.final_date between to_date('''|| crec.start_date ||''',''dd/mm/yyyy'') and to_date('''|| crec.end_date ||''',''dd/mm/yyyy'')';
v_where_clause := v_where_clause||v_condition;
end if;
end loop;
IF v_lp_cnt>0
THEN
v_where_clause := v_where_clause|| ')';
END IF;
--postcode
v_condition := NULL;
v_lp_cnt := 0;
FOR crec IN(SELECT (t.postcode) postcode
FROM XMLTABLE('/Filter/pc/postcode'
PASSING xmltype(p_filter)
COLUMNS postcode VARCHAR2(30)PATH '/postcode')t
WHERE 1 = 1
)
loop
v_lp_cnt := v_lp_cnt + 1;
IF v_lp_cnt = 1 THEN
v_condition := ' AND (postcode in ('''|| crec.postcode ||'''';
v_where_clause := v_where_clause||v_condition;
v_condition := NULL;
ELSE
v_condition := v_condition||','||''''|| crec.postcode ||'''';
v_where_clause := v_where_clause||v_condition;
v_condition := NULL;
END IF;
END LOOP;
IF v_lp_cnt >0
THEN
v_where_clause := v_where_clause||v_condition||'))';
end if;
v_query := v_query || v_where_clause;
execute immediate v_query into v_result;
p_result := v_result;
end;

Create a NZPLSQL to find min(date) and a max(date) for a group of fields that serve as a key

I would like to know how I can execute a select from a procedure stored in Netezza.
I currently have the procedure under construction and I read that the raise notice serves to print on screen, however I do not know how (or if you can print a table in sql) just as if you were doing a query in traditional sql.
What the query is trying to do is that for a record,
COUNT (*) AS N_REPETITIONS,
CDS.CASO_PACIENTE_FK,
CDS.RESIVALITY_SPECIALITY_COD,
CDS.CASO_NIVEL_ATENCION_TIPO,
CDS.CASO_TIPO_PRESTACION_FK,
CDS.DIVING_DATE_DAY,
CDS.SALID_DATE_DAY
and that it gives me MIN(CDS.DERIVACION_FECHA_HORA) and MAX(CDS.SALIDA_FECHA_HORA) based on a key that are the fields.
If I have dates between MIN(CDS.DERIVACION_FECHA_HORA) and MAX(CDS.SALIDA_FECHA_HORA) for a CDS.CASO_PACIENTE_FK, i want that register added to number of register for each CDS.CASO_PACIENTE_FK
Here the code:
CREATE OR REPLACE PROCEDURE SP_ANALISIS_DUPLICADOS()
RETURNS REFTABLE(NETEZZA_PROD_LISTA_ESPERA_NOGES.LLAVE_CASOS_DUPLICADOS)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
num_args INTEGER;
I am just starting to work with Netezza.
Thank you in advance to whom you answer.
This is the code:
CREATE OR REPLACE PROCEDURE SP_ANALISIS_DUPLICADOS()
RETURNS REFTABLE(NETEZZA_PROD_LISTA_ESPERA_NOGES.LLAVE_CASOS_DUPLICADOS)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
num_args INTEGER;
typ oid;
idx INTEGER;
N_REPETICIONES NUMERIC(19);
CASO_PACIENTE_FK NUMERIC(19);
DERIVACION_ESPECIALIDAD_COD VARCHAR(255);
CASO_NIVEL_ATENCION_TIPO NUMERIC(19);
CASO_TIPO_PRESTACION_FK NUMERIC(19);
DERIVACION_FECHA_HORA TIMESTAMP;
SALIDA_FECHA_HORA TIMESTAMP;
--CURSOR FECHA_HORA_DERIVACION_MIN;
--CURSOR FECHA_HORA_SALIDA_MAX;
CURSOR CASO_PACIENTE_FK;
CURSOR DERIVACION_ESPECIALIDAD_COD;
CURSOR CASO_NIVEL_ATENCION_TIPO;
CURSOR CASO_TIPO_PRESTACION_FK;
--L_CONDITIONS VARCHAR(1000);
--Duplicados CNE MIN
BEGIN
num_args := PROC_ARGUMENT_TYPES.count;
FOR CASO_PACIENTE_FK IN
num_args := CASO_PACIENTE_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for i IN 0
CASO_PACIENTE_FK.count - 1 LOOP
typ := CASO_PACIENTE_FK(i);
idx := i+1;
FOR DERIVACION_ESPECIALIDAD_COD IN
num_args := DERIVACION_ESPECIALIDAD_COD.count;
for j IN 0
DERIVACION_ESPECIALIDAD_COD.count - 1 LOOP
typ := DERIVACION_ESPECIALIDAD_COD(j);
idx := j+1;
FOR CASO_NIVEL_ATENCION_TIPO IN
num_args := CASO_PACIENTE_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for k IN 0
CASO_NIVEL_ATENCION_TIPO.count - 1 LOOP
typ := CASO_NIVEL_ATENCION_TIPO(k);
idx := k+1;
FOR CASO_TIPO_PRESTACION_FK IN
num_args := CASO_TIPO_PRESTACION_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for a IN 0
CASO_TIPO_PRESTACION_FK.count - 1 LOOP
typ := CASO_TIPO_PRESTACION_FK(a);
idx := a+1;
--RAISE NOTICE ’argument $% is type % and has the value ’’%’’’,
--idx, typ, $idx;
F FLAG = 1 THEN
IF Fecha_Entrada = SELECT COUNT(*) AS N_REPETICIONES, MIN(CDS.DERIVACION_FECHA_HORA) AS FECHA_HORA_DERIVACION_MIN
FROM NETEZZA_PROD_SIGTE_DIARIO.SIG_CASO_DERIVACION_SALIDA CDS
WHERE (CDS.CASO_TIPO_PRESTACION_FK=1
OR CDS.CASO_TIPO_PRESTACION_FK=2)
--AND CDS.DERIVACION_FECHA_HORA >= CDS.DERIVACION_FECHA_HORA
GROUP BY CDS.CASO_PACIENTE_FK, CDS.DERIVACION_ESPECIALIDAD_COD, /*CDS.DERIVACION_ESTABLECIMIENTO_COD*/
CDS.CASO_NIVEL_ATENCION_TIPO, /*CDS.DERIVACION_FECHA_HORA,*/ CDS.CASO_TIPO_PRESTACION_FK, CDS.DERIVACION_FECHA_HORA,
CDS.SALIDA_FECHA_HORA
HAVING COUNT(*)>1
ORDER BY N_REPETICIONES, CDS.CASO_PACIENTE_FK, CDS.DERIVACION_FECHA_HORA, CDS.SALIDA_FECHA_HORA DESC
THEN Fecha_Entrada = 'TRUE';
ELSE FLAG = 0 THEN
Fecha_Entrada = 'FALSE'
F FLAG = 1 THEN
IF Fecha_Salida = SELECT COUNT(*) AS N_REPETICIONES, MAX(CDS.SALIDA_FECHA_HORA) AS FECHA_HORA_DERIVACION_MIN
FROM NETEZZA_PROD_SIGTE_DIARIO.SIG_CASO_DERIVACION_SALIDA CDS
WHERE (CDS.CASO_TIPO_PRESTACION_FK=1
OR CDS.CASO_TIPO_PRESTACION_FK=2)
--AND CDS.DERIVACION_FECHA_HORA >= CDS.DERIVACION_FECHA_HORA
GROUP BY CDS.CASO_PACIENTE_FK, CDS.DERIVACION_ESPECIALIDAD_COD, /*CDS.DERIVACION_ESTABLECIMIENTO_COD*/
CDS.CASO_NIVEL_ATENCION_TIPO, /*CDS.DERIVACION_FECHA_HORA,*/ CDS.CASO_TIPO_PRESTACION_FK, CDS.DERIVACION_FECHA_HORA,
CDS.SALIDA_FECHA_HORA
HAVING COUNT(*)>1
ORDER BY N_REPETICIONES, CDS.CASO_PACIENTE_FK, CDS.DERIVACION_FECHA_HORA, CDS.SALIDA_FECHA_HORA DESC
THEN Fecha_Salida = 'TRUE';
ELSE FLAG = 0 THEN
Fecha_Salida = 'FALSE'
END LOOP;
END LOOP;
END LOOP;
END LOOP;
RETURN REFTABLE;
RAISE NOTICE 'Hello, %', MYNAME; --Right Here must go the Println (Select field1, field2, etc.....)
--END;
--COMMIT;
END;
END_PROC;

Fetching cursor data into array in PL/SQL

I have a problem at one exercise: I have a table with an ussualy column, and another column which is another table, like this:
CREATE OR REPLACE TYPE list_firstnames AS TABLE OF VARCHAR2(10);
then I create the following table:
CREATE TABLE persons (last_name VARCHAR2(10), first_name list_firstnames)
NESTED TABLE first_name STORE AS lista;
I insert:
INSERT INTO persons VALUES('Stewart', list_firstnames('John', 'Jack'));
INSERT INTO persons VALUES('Bauer', list_firstnames('Helen', 'Audrey'));
INSERT INTO persons VALUES('Obrian', list_firstnames('Mike', 'Logan'));
I want to make a cursor to take all last and first names from persons, and then I want to put all of them in an array. After this, I want to count the first names which contains the 'n' letter.
First of all, I want to know how I can put all the information from the cursor in an array. I try this:
DECLARE
CURSOR firstnames_students IS
select last_name,COLUMN_VALUE as "FIRSTNAME" from persons p, TABLE(p.last_name) p2;
v_lastname VARCHAR(50);
v_firstname VARCHAR(50);
v_I NUMBER := 1;
v_count NUMBER := 0;
v_contor INTEGER := 0;
TYPE MyTab IS TABLE OF persons%ROWTYPE INDEX BY VARCHAR2(20);
std MyTab;
BEGIN
OPEN firstnames_students;
LOOP
FETCH firstnames_students INTO v_lastname, v_firstname;
EXIT WHEN firstnames_students%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_lastname || ' ' || v_firstname);
END LOOP;
--select * INTO std FROM firstnames_students;
CLOSE firstnames_students;
END;
SET SERVEROUTPUT ON;
DECLARE
CURSOR students IS
SELECT * FROM persons;
v_row PERSONS%ROWTYPE;
v_names SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
v_count INT := 0;
BEGIN
OPEN students;
LOOP
FETCH students INTO v_row;
EXIT WHEN students%NOTFOUND;
v_names.EXTEND;
v_names(v_names.COUNT) := v_row.last_name;
FOR i IN 1 .. v_row.first_name.COUNT LOOP
v_names.EXTEND;
v_names(v_names.COUNT) := v_row.first_name(i);
END LOOP;
END LOOP;
CLOSE students;
FOR i IN 1 .. v_names.COUNT LOOP
IF INSTR( v_names(i), 'n' ) > 0 THEN
v_count := v_count + 1;
DBMS_OUTPUT.PUT_LINE( v_names(i) );
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE( v_count );
END;
Output:
John
Helen
Obrian
Logan
4

Any alternatives to using cursor in SQL procedure in Oracle 10g?

I give the SQL few inputs and I need to get all the ID's and their count that doesn't satisfy the required criteria.
I would like to know if there are there any alternatives to using cursor.
DECLARE
v_count INTEGER;
v_output VARCHAR2 (1000);
pc table1%ROWTYPE;
unmarked_ids EXCEPTION;
dynamic_sql VARCHAR (5000);
cur SYS_REFCURSOR;
id pp.id%TYPE;
pos INTEGER;
BEGIN
v_count := 0;
SELECT *
INTO pc
FROM table1
WHERE id = '&ID';
DBMS_OUTPUT.ENABLE;
dynamic_sql :=
'SELECT ID from pp
WHERE ( TO_CHAR(cdate, ''yyyy/mm/dd'') =
TO_CHAR (:a, ''yyyy/mm/dd''))
AND aid IN (SELECT aid FROM ppd WHERE TO_CHAR(cdate, ''yyyy/mm/dd'') =
TO_CHAR (:b, ''yyyy/mm/dd'')
AND cid = :c )
AND cid <> :d';
OPEN cur FOR dynamic_sql USING pc.cdate, pc.cdate, pc.id, pc.id;
LOOP
FETCH cur INTO id;
EXIT WHEN cur%NOTFOUND;
v_count := v_count + 1;
DBMS_OUTPUT.PUT_LINE (' Id:' || id);
END LOOP;
CLOSE cur;
IF (v_count > 0)
THEN
DBMS_OUTPUT.PUT_LINE ( 'Count: ' || v_count || ' SQL: ' || dynamic_sql);
RAISE unmarked_ids;
END IF;
DBMS_OUTPUT.PUT_LINE('SQL ended successfully');
EXCEPTION
WHEN unmarked_ids
THEN
DBMS_OUTPUT.put_line (
'Found ID's that not marked with the current id.');
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line (
'No data found in table1 with the current id ' || '&ID');
END;
There are bind variables in the query. One of them is date, there are three more.
The count and ID's are required to be shown which will later be reported.
You could store the rowid in a temporary table along with an index value (0...n) and then use a while loop to go through the index values and join to the real table using the rowid.