I have a pipe line function like below. if I want to delete some rows from the fetched output, how should I do that.
I tried myObjectFormat.delete after the END LOOP in below code.
but it didn't work.
Please let me know what I am doing wrong;
CREATE OR REPLACE TYPE myObjectFormat AS OBJECT
(
A INT,
B DATE,
C VARCHAR2 (25)
)
/
CREATE OR REPLACE TYPE myTableType AS TABLE OF myObjectFormat
/
CREATE OR REPLACE FUNCTION SB360.test_func (P_ID VARCHAR2)
RETURN myTableType
PIPELINED
IS
v_obj myObjectFormat := myObjectFormat (NULL, NULL, NULL);
v_cur SYS_REFCURSOR;
QUERY_STMT VARCHAR2 (32767);
A INT;
B DATE;
C VARCHAR2 (25);
BEGIN
QUERY_STMT := 'select a,b,c from test where id=' || P_ID;
OPEN V_CUR FOR QUERY_STMT;
LOOP
FETCH V_CUR INTO a, b, c;
EXIT WHEN V_CUR%NOTFOUND;
v_obj.A := A;
v_obj.b := b;
v_obj.c := c;
PIPE ROW (v_obj);
END LOOP;
RETURN;
END;
/
Related
Is it possible to fetch cursor data into array or table with dynamic cursor parameters ?
Example : We have 3 function fct1(), fct2(), fct3(). They all return a CURSOR with different data (from different table) and their size is between 20 and 100.
I'd like to call this 3 function with a generic function, fill a VARCHAR2 array and for example print this array.
I've found out how to fetch cursor into a VARRAY or VARCHAR2 but then you need to specify which cursor value you add into the array.
CREATE OR REPLACE PACKAGE BODY CURSOR_EXAMPLE AS
FUNCTION fct1 (param01 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT a, b, c FROM table1, table2, table3;
RETURN my_cursor;
END fct1;
FUNCTION fct2 (param01 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT q, w, e, r, t, y FROM table4, table5;
RETURN my_cursor;
END fct2;
FUNCTION fct3 (param01 IN VARCHAR2, param02 IN VARCHAR2, param03 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT x, y, z FROM table6, table7, table8, table8;
RETURN my_cursor;
END fct3;
PROCEDURE generic_function (fct IN NUMBER, param01 IN VARCHAR2, param02 IN VARCHAR2, param03 IN VARCHAR2, cursor_out OUT SYS_REFCURSOR)
AS
BEGIN
IF fct = 1
cursor_out := fct1(param01);
ELSE IF fct = 2
cursor_out := fct2(param01);
ELSE IF
cursor_out := fct3(param01, param02, param03);
END IF;
LOOP
FETCH cursor_out INTO
-- HERE DEPENDING ON fct1, 2 and 3 add cursor_out variable into a VARRAY (extend it as needed)
EXIT WHEN cursor_out%NOTFOUND;
END LOOP;
-- PRINT VARRAY
END generic_function;
END CURSOR_EXAMPLE;
It appears that you are trying to devise a dynamic means of discovering what columns are contained within the cursor.
There is a way, but it involves using DBMS_SQL and once you convert the cursor to a DBMS_SQL cursor, you will have to use DBMS_SQL all the way as you process the records.
Here is an example of a test function that can return one of four different cursors, without static defined types:
CREATE OR REPLACE FUNCTION get_test_cursor (n in number default 1) return SYS_REFCURSOR
IS
c SYS_REFCURSOR;
BEGIN
IF n = 1
THEN
OPEN c FOR SELECT 'A' f1, 'B' f2 FROM DUAL;
END IF;
IF n = 2
THEN
OPEN c FOR SELECT cast(1.234 as number(6,2)) n1, cast(12 as number(8)) n2, 'C' f3 FROM DUAL;
END IF;
IF n = 3
THEN
OPEN c FOR select * from all_objects;
END IF;
if n = 4
then
OPEN c FOR select sysdate as current_dt from dual;
end if;
return c;
END;
/
Now, we can use the DBMS_SQL package to first convert the cursor from a REF CURSOR to a cursor number using dbms_sql.to_cursor_number.
Once we do this, we can use the rest of the DBMS_SQL API to inspect the cursor and do work on its data.
declare
cur sys_refcursor;
c number;
col_cnt INTEGER;
rec_tab DBMS_SQL.DESC_TAB;
--
-- This is a crude helper procedure to display one line of "DESCRIBE" output
--
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(rpad(rec.col_name, 41) || ' ' ||
rpad(case when rec.col_null_ok then ' ' else 'NOT NULL' end, 8) || ' ' ||
case when rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR or rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR2 then
'VARCHAR2(' || rec.col_max_len || ')'
when rec.col_type = DBMS_TYPES.TYPECODE_CHAR then
'CHAR(' || rec.col_max_len || ')'
when rec.col_type = DBMS_TYPES.TYPECODE_NUMBER then
case when rec.col_precision = 0 and rec.col_scale = -127 then 'NUMBER'
when rec.col_scale = 0 then 'NUMBER('||rec.col_precision||')'
else 'NUMBER(' || rec.col_precision || ', ' || rec.col_scale || ')'
end
when rec.col_type = DBMS_TYPES.TYPECODE_DATE then
'DATE'
else
'UNKNOWN'
end);
END;
begin
cur := test_cursor(3);
--
-- Convert the REF_CUR to a cursor number
--
c := dbms_sql.to_cursor_number(cur);
--
-- Use an API call to describe the columns
--
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
--
-- Now loop through the columns and show them
--
dbms_output.put_line('Name Null? Type');
dbms_output.put_line('----------------------------------------- -------- ----------------------------');
for j in 1..col_cnt loop
print_rec(rec_tab(j));
end loop;
--
-- We can do other things at this point.
-- When done, close the cursor.
--
DBMS_SQL.CLOSE_CURSOR(c);
end;
/
Here is the output when we pass in 3, which queries the ALL_OBJECTS view:
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER NOT NULL VARCHAR2(128)
OBJECT_NAME NOT NULL VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(128)
OBJECT_ID NOT NULL NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(23)
CREATED NOT NULL DATE
LAST_DDL_TIME NOT NULL DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
NAMESPACE NOT NULL NUMBER
EDITION_NAME VARCHAR2(128)
SHARING VARCHAR2(13)
EDITIONABLE VARCHAR2(1)
ORACLE_MAINTAINED VARCHAR2(1)
Once you are able to see what the columns of the cursor are, you are on your way to solving the problem of creating a dynamic procedure that consumes random cursors and populates arrays.
I have 6 small functions and want to call all 6 functions in one main function
Like in Main function want to call 6 functions:
v_CASE_A := FUNCTION_1
v_CASE_B := FUNCTION_2
And so on.
Example This is small function_1:
CREATE OR REPLACE FUNCTION u_t (v_number IN NUMBER)
RETURN VARCHAR2
IS
v_length NUMBER;
result VARCHAR2 (100);
v_figure VARCHAR2 (100);
BEGIN
v_length := LENGTH (v_number);
v_figure := TO_CHAR (TO_DATE (v_number, 'J'), 'JSP');
IF v_length IN (1, 2)
THEN
--**result := i want to call v_case_a in this variable from main function;**
END IF;
RETURN RESULT;
END;
Example This is main function:
CREATE OR REPLACE FUNCTION number_to_words (v_number IN NUMBER)
RETURN VARCHAR2
IS
v_result VARCHAR2 (500);
v_figure VARCHAR2 (100);
v_case_A VARCHAR2 (100);
BEGIN
v_result := v_case_A;
v_figure := TO_CHAR (TO_DATE (v_number, 'J'), 'JSP');
--**v_case_A := i want to call function_1 in this variable;**
RETURN v_result;
END;
How to call multiple functions in one main function PL/SQL?
I'm not sure what you want to do, but it sounds like this:
CREATE OR REPLACE FUNCTION number_to_words (v_number IN NUMBER)
RETURN VARCHAR2
IS
v_result VARCHAR2 (500);
BEGIN
-- classical if-block
IF v_number = 1
THEN
v_result := function1 ();
ELSIF v_number = 2
THEN
v_result := function2 ();
ELSE
v_result := functionDefaultBehaviour ();
END IF;
-- case-block
CASE v_number
WHEN 1 THEN
v_result := function1 ();
WHEN 2 THEN
v_result := function2 ();
ELSE
v_result := functionDefaultBehaviour ();
END CASE;
RETURN v_result;
END;
v_result is set to the return-values of function1, function2 and functionDefaultBehaviour depending on the parameter v_number.
This is run two times - by a if-else-block and a second time with the more beautifil case-statements.
I am using Oracle 12, and I want to make a dynamic procedure which selects rows from specific table but according to an unknown conditio. That condition will be specified as input parameter.
Suppose I have a column called employee id and I want to call the procedure
with the following condition
execute s('employeeid = 2')
My code is
create or replace procedure s (condition varchar)
as
TYPE EmpCurTyp IS REF CURSOR; -- define weak REF CURSOR type
emp_cv EmpCurTyp; -- declare cursor variable
my_ename VARCHAR2(15);
my_sal NUMBER := 2;
mycondition varchar2(100):=condition;
BEGIN
OPEN emp_cv FOR -- open cursor variable
'SELECT employeeid, employeename FROM employees WHERE = :s' USING mycondition;
END;
but I am getting an error
missing expression
What am I doing wrong, and will the result of this procedure be selected rows from employees table that satisfy applied condition ?
The USING is meant to handle values, not pieces of code; if you need to edit your query depending on an input parameter ( and I believe this is a very dangerous way of coding), you should treat the condition as a string to concatenate to the query.
For example, say you have this table:
create table someTable(column1 number)
This procedure does somthing similar to what you need:
create or replace procedure testDyn( condition IN varchar2) is
cur sys_refcursor;
begin
open cur for 'select column1 from sometable where ' || condition;
/* your code */
end;
Hot it works:
SQL> exec testDyn('column1 is null');
PL/SQL procedure successfully completed.
SQL> exec testDyn('column99 is null');
BEGIN testDyn('column99 is null'); END;
*
ERROR at line 1:
ORA-00904: "COLUMN99": invalid identifier
ORA-06512: at "ALEK.TESTDYN", line 4
ORA-06512: at line 1
This is not embedded in a procedure yet but I tested this and works:
DECLARE
TYPE OUT_TYPE IS TABLE OF VARCHAR2 (20)
INDEX BY BINARY_INTEGER;
l_cursor INTEGER;
l_fetched_rows INTEGER;
l_sql_string VARCHAR2 (250);
l_where_clause VARCHAR2 (100);
l_employeeid VARCHAR2 (20);
l_employeename VARCHAR2 (20);
l_result INTEGER;
o_employeeid OUT_TYPE;
o_employeename OUT_TYPE;
BEGIN
l_cursor := DBMS_SQL.OPEN_CURSOR;
l_sql_string := 'SELECT employeeid, employeename FROM employees WHERE ';
l_where_clause := 'employeeid = 2';
l_sql_string := l_sql_string || l_where_clause;
DBMS_SQL.PARSE (l_cursor, l_sql_string, DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
1,
l_employeeid,
20);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
2,
l_employeename,
20);
l_fetched_rows := 0;
l_result := DBMS_SQL.EXECUTE_AND_FETCH (l_cursor);
LOOP
EXIT WHEN l_result = 0;
DBMS_SQL.COLUMN_VALUE (l_cursor, 1, l_employeeid);
DBMS_SQL.COLUMN_VALUE (l_cursor, 2, l_employeename);
l_fetched_rows := l_fetched_rows + 1;
o_employeeid (l_fetched_rows) := l_employeeid;
o_employeename (l_fetched_rows) := l_employeename;
l_result := DBMS_SQL.FETCH_ROWS (l_cursor);
END LOOP;
DBMS_SQL.CLOSE_CURSOR (l_cursor);
DBMS_OUTPUT.PUT_LINE (o_employeeid (1));
DBMS_OUTPUT.PUT_LINE (o_employeename (1));
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('GENERAL FAILURE: ' || SQLERRM);
END;
I need to return the table as a result of this select. The argument passed to the function opredelyaet from which an array of associative data will be displayed as a table:
select * from table(task_2.get_con_coll('save'));
I wrote this code and to me it is correct and I do not see that I have missed, or that it is not paved with
Сreates an object
-- the creation of an array type
create or replace type con_coll_type is object(
id integer,
user_name varchar2(255));
Then he created the package
create or replace package task_2 is
/*
-- may need to be so?
type con_coll_type is record(
id integer,
user_name varchar(255));
-- */
--Create associative array
type con_coll_t is table of con_coll_type index by varchar2(255);
-- need using this
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined;
end task_2;
create or replace package body task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined is
indx varchar(255);
coll_edit con_coll_t;
coll_delete con_coll_t;
coll_save con_coll_t;
begin
-- Filling collection
coll_edit(1) := con_coll_type(1, 'some_name_1');
coll_edit(2) := con_coll_type(2, 'some_name_2');
coll_delete(3) := con_coll_type(3, 'some_name_3');
coll_delete(4) := con_coll_type(4, 'some_name_4');
coll_save(5) := con_coll_type(5, 'some_name_5');
coll_save(6) := con_coll_type(6, 'some_name_6');
-- If the parameter is passed to the function "Save" - a collection of output
if coll_name = 'save' then
indx := coll_save.first;
loop
exit when indx is null;
-- pipelined output
pipe row(con_coll_type(coll_save(indx).id,
coll_save(indx).user_name));
indx := coll_save.next(indx);
end loop;
end if;
end get_con_coll;
end task_2;
What's wrong with my code? I can not understand what I missed.
First, the Table type should be out of the package:
create type con_coll_t is table of con_coll_type;
And, if you have default values for your types, you can implement them like that:
coll_delete con_coll_t;
begin
coll_delete := con_coll_t(con_coll_type(3, 'some_name_3'),
con_coll_type(4, 'some_name_4'));
end;
With that being said, I think your general code should be something like that:
-- the creation of an array type
create or replace type con_coll_type is object(
id integer,
user_name varchar2(255));
create or replace type con_coll_t is table of con_coll_type;
create or replace package task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined;
end task_2;
create or replace package body task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined is
indx varchar(255);
coll_edit con_coll_t;
coll_delete con_coll_t;
coll_save con_coll_t;
begin
-- Filling collection
coll_edit := con_coll_t( con_coll_type(1, 'some_name_1')
, con_coll_type(2, 'some_name_2'));
coll_delete := con_coll_t( con_coll_type(3, 'some_name_3')
, con_coll_type(4, 'some_name_4'));
coll_save := con_coll_t( con_coll_type(5, 'some_name_5')
, con_coll_type(6, 'some_name_6'));
-- If the parameter is passed to the function "Save" - ??a collection of output
if coll_name = 'save' then
indx := coll_save.first;
loop
exit when indx is null;
-- pipelined output
pipe row(con_coll_type(coll_save(indx).id,
coll_save(indx).user_name));
indx := coll_save.next(indx);
end loop;
end if;
end get_con_coll;
end task_2;
That's what I wanted and what made
create or replace type con_coll_type is object(id integer,
user_name varchar2(255));
create or replace type con_coll_t is table of con_coll_type;
create or replace package task_2 is
type list_of_oper is table of con_coll_t index by varchar2(255);
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined;
end task_2;
create or replace package body task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined is
indx varchar(255);
indx_2 varchar(255);
coll_main list_of_oper;
begin
coll_main('edit') := con_coll_t(con_coll_type(1, 'some_name_1'),
con_coll_type(2, 'some_name_2'));
coll_main('delete') := con_coll_t(con_coll_type(3, 'some_name_3'),
con_coll_type(4, 'some_name_4'));
coll_main('save') := con_coll_t(con_coll_type(5, 'some_name_5'),
con_coll_type(6, 'some_name_6'));
indx := coll_main.first;
loop
exit when indx is null;
if indx = coll_name then
indx_2 := coll_main(indx).first;
loop
exit when indx_2 is null;
pipe row(coll_main(indx)(indx_2));
/*pipe row(con_coll_type(coll_main(indx)(indx_2).id,
coll_main(indx)(indx_2).user_name));*/
indx_2 := coll_main(indx).next(indx_2);
end loop;
end if;
indx := coll_main.next(indx);
end loop;
return;
end get_con_coll;
end task_2;
select *
from table(task_2.get_con_coll('edit'))
union all
select *
from table(task_2.get_con_coll('delete'))
union all
select *
from table(task_2.get_con_coll('save'));
I writing this procedure and I have question how can I overwrite old value on new Value and return the new line with new big one string with new mails.
Procedure split one big email which have got emails on few single mails and change domain.
Perception: I got one table with atributes example Values which have one big string with emails. I must change this emails to domain NewDomain.pl when its diffrent from aaa.pl and bbb.pl when its the same I leave this emails. example:
Old Value: 'zamowienia#kicius.pl mickey.mouse#aaa.pl kimus.walus#domek.pl'
and result I want update:
**New Value: 'zamowienia#NewDomain.pl mickey.mouse#aaa.pl kimus.walus#NewDomain.pl'
First procedure:
CREATE OR REPLACE PROCEDURE changeMailAll
AS
BEGIN
DECLARE delim char(3);
cur_position INTEGER(11) DEFAULT 1;
r_remainder VARCHAR(250);
cur_string VARCHAR(1000);
delim_length INTEGER;
length_remainder INTEGER;
mail VARCHAR(255);
MAILs VARCHAR(20000);
v_value VARCHAR2(255);
v_valueNew VARCHAR2(255);
v_customerId VARCHAR(20);
c INTEGER;
d INTEGER;
positionMonkey INTEGER;
v_identity VARCHAR(50);
domena VARCHAR2(50);
v_loop VARCHAR(100);
adres VARCHAR(255);
**str PKT_StringFnc.t_array;**
CURSOR cursorMails IS
SELECT Customer_Id, Value FROM PKT_userTrue where method_id = 'E_MAIL';
BEGIN
OPEN cursorMails;
LOOP
FETCH cursorMails INTO v_customerId, v_value;
**str := PKT_StringFnc.SPLIT(v_value,' ');
FOR i IN 1..str.COUNT LOOP
dbms_output.put_line('XXX1' || str(i));
END LOOP;**
EXIT WHEN cursorMails%NOTFOUND;
END LOOP;
CLOSE cursorMails;
END;
END;
End Second procedure where I split mail from first procedure
CREATE OR REPLACE PACKAGE BODY PKT_StringFnc
IS
FUNCTION SPLIT (p_in_string VARCHAR2, p_delim VARCHAR2) RETURN t_array
IS
i number :=0;
pos number :=0;
lv_str varchar2(255) := p_in_string;
positionMonkey INTEGER;
domena VARCHAR2(50);
adres VARCHAR(255);
lv_str_new VARCHAR2(255);
aaa VARCHAR(20) := '#aaa.pl ';
bbb VARCHAR(30) := '#bbb.pl ';
result VARCHAR(1000);
strings t_array;
BEGIN
pos := instr(lv_str,p_delim,1,1);
WHILE ( pos != 0) LOOP
i := i + 1;
strings(i) := substr(lv_str,1,pos);
positionMonkey := INSTR(strings(i),'#');
domena := SUBSTR(strings(i), positionMonkey);
adres := RTRIM(strings(i),domena);
lv_str := substr(lv_str,pos+1,length(lv_str));
pos := instr(lv_str,p_delim,1,1);
IF pos = 0 THEN
strings(i+1) := lv_str;
ELSE
strings(i+1) := lv_str_new;
END IF;
IF domena = aaa OR domena = bbb THEN
lv_str_new := REPLACE(strings(i),domena,'#NewDomain.com');
END IF;
DBMS_OUTPUT.PUT_LINE('lv_str_newREPLACE:'|| lv_str_new);
END LOOP;
RETURN strings;
END SPLIT;
END;
/
When I return one big string I want update in table and where I can do it ?
Thanks for help
Maybe somebody can rewrite easiest procedure from two to one procedure
I wonder if something like
UPDATE pktTrue
SET Value = REGEXP_REPLACE(Value, '(#aaa.pl)|(#bbb.pl)|(#[a-zA-Z0-9._%-]+\.[a-zA-Z]{2,4})', '\1\2#NewDomain.pl', 1, 0, 'c')
WHERE method_id = 'E_MAIL';
will work for you?