Procedure and function. Return one big string - oracle

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?

Related

Oracle loop through CLOB to get string

I want to convert all data in CLOB to string. DBMC_LOB.SUBSTR provides a way to fetch 4000 characters at once. I am from MS SQL background and not aware of Oracle queries. Can someone help me with the syntax.
I want to make a function and do something like
// Get the length of CLOB
// totalIterationsRequired = length/4000;
// LOOP around the CLOB and fetch 4000 char at once
For i in 1..totalIterationsRequired
LOOP
// Insert the substring into a varchar2
// DBMC_LOB.SUBSTR(xml_value,4000*i,(4000*(i-1)+1)
END LOOP
// The function will return the varchar2
create table save_table(rec varchar2(4000));
create or replace PROCEDURE save_clob (p_clob IN CLOB)
AS
l_length INTEGER;
l_start INTEGER := 1;
l_recsize CONSTANT INTEGER := 4000;
BEGIN
l_length := DBMS_LOB.getlength (p_clob);
WHILE l_start <= l_length
LOOP
INSERT INTO save_table (rec)
VALUES (DBMS_LOB.SUBSTR (p_clob, l_recsize, l_start));
l_start := l_start + l_recsize;
END LOOP;
END save_clob;
Below is the function definition
create or replace function getclobastable (id IN VARCHAR2)
return clob_table
is
l_length INTEGER;
l_start INTEGER := 1;
l_recsize CONSTANT INTEGER := 4000;
i_clob CLOB;
n BINARY_INTEGER := 0;
save_table clob_table := clob_table();
BEGIN
save_table := clob_table();
-- Get the CLOB data into i_clob
l_length := DBMS_LOB.getlength (i_clob);
WHILE l_start <= l_length
LOOP
n := n + 1;
save_table.EXTEND();
save_table(n) := DBMS_LOB.SUBSTR (i_clob, l_recsize, l_start);
l_start := l_start + l_recsize;
END LOOP;
RETURN save_table;
END;

Printing Non Repeating Characters first

I am new to PL/SQL and I am trying to write a procedure which would print the non repeating characters of an input string first and Repeating characters of the string in the last. For example if the input string is "Array" then the output should be "yArra".
I wrote a part of it for searching the no. of occurrences of a repeating character, but don't know how exactly should it be printed at the first place.
I wrote an algorithm on this on how can this be made to work but finding difficult to code
Thanks in advance for your help!
I am trying to write a procedure which would print the non repeating
characters of an input string first and Repeating characters of the
string in the last.
You can do this using a pure PLSQL code as below:
create or replace procedure prnt_letter(strng varchar2) as
var varchar2(1);
var1 varchar2(1000) := '';
var2 varchar2(1);
var3 varchar2(1000) := '';
strn_len number;
begin
dbms_output.put_line('Input String --> ' || strng);
strn_len := length(strng);
var := substr(strng, 1, 1);
for i in 1 .. strn_len loop
if (var = substr(strng, i, 1)) then
var2 := substr(strng, i, 1);
var3 := var3 || var2;
var := substr(strng, i, 1);
else
var1 := var1 || substr(strng, i, 1);
var := substr(strng, i, 1);
end if;
end loop;
dbms_output.put_line('Output String --> '||var1 || var3);
end;
EDIT:
Here is my revised solution both in PLSQL and SQL. This works for any string.
PLSQL:
create or replace procedure prnt_letter(strng varchar2) as
var1 varchar2(1000) := '';
strn_len number;
begin
dbms_output.put_line('Input String --> ' || strng);
strn_len := length(strng);
SELECT reverse (LISTAGG (vertical, '') WITHIN GROUP (ORDER BY 1 DESC))
into var1
FROM (
SELECT SUBSTR (strng, LEVEL, 1) Vertical
FROM DUAL
CONNECT BY LEVEL <= strn_len
) ;
dbms_output.put_line('Output String --> '||var1 );
end;
Output:
SQL> execute prnt_letter('rajjjjkkmmaaljjjl');
Input String --> rajjjjkkmmaaljjjl
Output String --> rmmllkkjjjjjjjaaa
PL/SQL procedure successfully completed.
SQL> execute prnt_letter('bubble');
Input String --> bubble
Output String --> ulebbb
PL/SQL procedure successfully completed.
SQL:
-- Logic used:
1) The input string is first arranged vertically in separate rows and
then ordered
2) Using LISTAGG, the result was assembled as a single ordered string
3) Using REVERSE the non-repeating string is brought to the starting
of the string.
SELECT reverse (LISTAGG (vertical, '') WITHIN GROUP (ORDER BY 1 DESC)) col1
FROM ( SELECT SUBSTR ('rajjjjkkmmaaljjjl', LEVEL, 1) Vertical
FROM DUAL
CONNECT BY LEVEL <= LENGTH ('rajjjjkkmmaaljjjl')
)
Try and use the REGEXP_COUNT function for the same. You can first provide a filter where this result >1 to find repeating characters and then concatenate them with the ones whose count = 1.
Check how to use regexp_count
I think the solution can be acheived by just using pure SQL rather than using PLSQL. Hope below snippet helps.
SELECT a.COL
||REPLACE('&Enter_text',a.col,'') output
FROM
(SELECT regexp_count('&Enter_text',SUBSTR('&Enter_text',level,1)) col1,
SUBSTR('&Enter_text',level,1) col
FROM DUAL
CONNECT BY level <=LENGTH('&Enter_text')
)a
WHERE a.col1 = 1;
That's fun, so I came out with something easily understandable using associative arrays as Hashmap; there's something subtle also with the non-case-sensitiveness:
CREATE OR REPLACE FUNCTION f(p_str in varchar2)
RETURN varchar2
AS
TYPE map_v IS TABLE OF integer INDEX BY varchar2(1);
l_dup map_v;
i PLS_INTEGER;
l_c varchar2(1);
l_tc varchar2(1);
l_nb_occurrences integer := NULL;
l_out_sngl varchar2(2000) := '';
l_out_dupl varchar2(2000) := '';
BEGIN
-- l_dup('a'):=0;
-- l_dup('b'):=0;
-- first loop to count occurrences
i:=1;
LOOP
l_c := lower(substr(p_str, i, 1));
begin
l_nb_occurrences := l_dup(l_c);
l_dup(l_c) := l_nb_occurrences + 1;
dbms_output.put_line(l_c||':incr:'||i);
exception
when no_data_found then
l_dup(l_c) := 1;
dbms_output.put_line(l_c||':pushed:'||i);
when others then
raise;
end;
i := i+1;
EXIT WHEN i > length(p_str);
END LOOP;
-- second loop for building output
i:=1;
LOOP
l_c := lower(substr(p_str, i, 1));
l_tc := substr(p_str, i, 1);
begin
l_nb_occurrences := l_dup(l_c);
dbms_output.put_line(l_c||':xx:'||i||'||'||l_nb_occurrences);
if l_nb_occurrences = 1 then
l_out_sngl := l_out_sngl || l_tc;
else
l_out_dupl := l_out_dupl || l_tc;
end if;
exception
when no_data_found then
dbms_output.put_line('why? there should be (see first loop).');
when others then
raise;
end;
i := i+1;
EXIT WHEN i > length(p_str);
END LOOP;
return l_out_sngl || l_out_dupl;
exception
when others then
dbms_output.put_line(sqlerrm);
END f;
/
Which gives results:
select f('Array') from dual;
-- yArra
select f('Bubbles') from dual;
-- ulesBbb
Try this function...
CREATE OR REPLACE FUNCTION non_repeating_char_first (v_input IN varchar2)
RETURN varchar2
IS
str1 varchar2 (100);
str2 varchar2 (100);
v_match number;
v_output varchar2 (100);
BEGIN
str1 := '';
str2 := '';
FOR i IN 1 .. LENGTH (v_input)
LOOP
SELECT REGEXP_COUNT (v_input,
SUBSTR (v_input, i, 1),
1,
'i')
INTO v_match
FROM DUAL;
IF v_match =1 THEN
str1 :=str1||SUBSTR (v_input, i, 1);
else
str2 :=str2||SUBSTR (v_input, i, 1);
END IF;
END LOOP;
v_output:=str1||str2;
RETURN v_output;
END;

get key and value of associative arrays

My problem is that i want to get key and value from an assosiative array, but i only can find how to get the value from the key. This is what i find:
DECLARE
TYPE assoc_array IS TABLE OF VARCHAR2(30)
INDEX BY VARCHAR2(30);
state_array assoc_array;
BEGIN
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
dbms_output.put_line(state_array('Alaska'));
dbms_output.put_line(state_array('California'));
END;
This prints Juneau and Sacramento, but i want something like this:
DECLARE
TYPE assoc_array IS TABLE OF VARCHAR2(30)
INDEX BY VARCHAR2(30);
state_array assoc_array;
BEGIN
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
for x in 1..state_array.count loop
dbms_output.put_line(state_array(x).key || state_array(x).value);
end loop;
END;
Is that possible?. Thanks in advance!!
Actually there is a way, kindly consider the code bellow
declare
type assoc_array is table of varchar2(30) index by varchar2(30);
state_array assoc_array;
l_idx varchar2(30);
begin
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
l_idx := state_array.first;
while (l_idx is not null) loop
dbms_output.put_line('Key = ' || l_idx || ':Value = ' || state_array(l_idx));
l_idx := state_array.next(l_idx);
end loop;
end;
The output will be
Key = Alaska:Value = Juneau
Key = California:Value = Sacramento
You cannot do this using associative arrays. Because, see below.
DECLARE
TYPE assoc_array IS TABLE OF VARCHAR2(30)
INDEX BY VARCHAR2(30);
state_array assoc_array;
BEGIN
state_array('Alaska') := 'Juneau';
state_array('California') := 'Sacramento';
for x in 1..state_array.count loop
dbms_output.put_line('x ='||x);
end loop;
END;
would print
x = 1
x = 2
There is no way for oracle to know that x = 1 = Alaska.
You should use binary array to do something like this.

custom split function not working in pl/sql

I have written a custom function in pl/sql which splits a clob variable into set of strings on the basis of the separator provided but it is not working as intended please help me out in figuring the problem with the code.
create or replace
FUNCTION SPLITCLOB (p_in_string clob, p_delim VARCHAR2) RETURN t_array
IS
i number :=0;
pos number :=0;
lv_str clob := p_in_string;
Strings t_array:=t_array ();
Begin
-- determine first chuck of string
pos := DBMS_LOB.INSTR(lv_str,p_delim,1,1);
If ( pos = 0) Then
i := i + 1;
strings.Extend();
strings(i) := DBMS_LOB.SUBSTR(lv_str,1);
RETURN strings;
End If;
-- while there are chunks left, loop
WHILE ( pos != 0) LOOP
-- increment counter
i := i + 1;
-- create array element for chuck of string
strings.EXTEND();
strings(i) := DBMS_LOB.SUBSTR(lv_str,1,pos-1);
-- remove chunk from string
lv_str := DBMS_LOB.SUBSTR(lv_str,pos+1,dbms_lob.getLength(lv_str));
-- determine next chunk
pos := DBMS_LOB.INSTR(lv_str,p_delim,1,1);
-- no last chunk, add to array
IF pos = 0 THEN
strings.extend();
strings(i+1) := lv_str;
END IF;
End Loop;
-- return array
RETURN strings;
End Splitclob;
here t_array() is custom type defiition of which is provided below
create or replace
TYPE "T_ARRAY"
AS TABLE OF VARCHAR2(500);
thanks in advance
I've changed a piece of code I wrote not a long time ago and it seems it works as you want. Note: it will also work if there is only a single item in the input parameter (I mean, the situation when there is no separator in the input is covered).
CREATE OR REPLACE
TYPE T_ARRAY
AS TABLE OF VARCHAR2(500);
/
CREATE OR REPLACE FUNCTION splitclob (p_clob_in CLOB, p_delim VARCHAR2) RETURN t_array
AS
v_buffer VARCHAR2(500);
v_len NUMBER;
v_offset NUMBER := 1;
v_delim_pos NUMBER;
v_amount NUMBER;
v_result_array t_array := t_array();
BEGIN
IF p_clob_in IS NOT NULL THEN
v_len := dbms_lob.getlength(p_clob_in);
WHILE v_offset < v_len
LOOP
v_delim_pos := instr(p_clob_in, p_delim, v_offset);
IF v_delim_pos = 0 THEN
v_amount := v_len - v_offset + 1;
ELSE
v_amount := v_delim_pos - v_offset;
END IF;
dbms_lob.read(p_clob_in, v_amount, v_offset, v_buffer);
v_offset := v_offset + v_amount + 1;
v_result_array.EXTEND;
v_result_array(v_result_array.LAST) := v_buffer;
END LOOP;
END IF;
RETURN v_result_array;
END;
/
DECLARE
v_array t_array;
BEGIN
v_array := splitclob('order_id=383325; order_line_item=59; order_class_id=5749; subscription_code=5U12PFNCAU; start_date=01/12/2012; end_date=30/11/2013; date_override=null; license_available=-1; license_consumed=1; license_override=-1; order_class_status=0', ';');
FOR v_i IN v_array.first..v_array.last
LOOP
dbms_output.put_line(v_i || ' ' || v_array(v_i));
END LOOP;
END;
/
Output:
1 order_id=383325
2 order_line_item=59
3 order_class_id=5749
4 subscription_code=5U12PFNCAU
5 start_date=01/12/2012
6 end_date=30/11/2013
7 date_override=null
8 license_available=-1
9 license_consumed=1
10 license_override=-1
11 order_class_status=0
You have to take into account a situation when one of the tokens will be longer than 500 characters. Remember that VARCHAR2 datatype in SQL context has a 4000 bytes limitation. If you know there won't be tokens longer than that, then you don't have to change anything.

Dynamic typing or generics in Oracle PL/SQL

For some reason, I have certain fields in a table, that are collections of chars. Chars of different length. Example:
create or replace type t_charray_1 as varray(5) of char(1);
create or replace type t_charray_2 as varray(5) of char(2);
create or replace type t_charray_3 as varray(5) of char(3);
create or replace type t_charray_4 as varray(5) of char(4);
create table mytable (
field1 number,
field2 t_charray_1,
field3 t_charray_3,
Also, I have a function that returns a (fixed length) string representation of a mytable record. This function calls other functions that are returning the string representation of a given collection-typed field. Examples:
function to_chr(
p_array in t_charray_1,
pad_length in number,
p_list_length in number
) return char as
v_res varchar2(255) := '';
begin
for i in 1 .. p_list_length loop
if p_array is not null and p_array.exists(i) and p_array(i) is not null then
v_res := v_res || rpad(p_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
------------------------------------------------------------------------------
function to_chr(
p_array in t_charray_2,
pad_length in number,
p_list_length in number
) return char as
v_res varchar2(255) := '';
begin
for i in 1 .. p_list_length loop
if p_array is not null and p_array.exists(i) and p_array(i) is not null then
v_res := v_res || rpad(p_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
Note that these functions are overloaded versions of each other. The only difference in their signature is the type of the p_array argument.
Please also note that the bodies of these functions are identical.
Motivation
I want to eliminate duplicate code. What are my choices?
EDIT I have heard of sys.anydata but never used it. Can it be a solution?
You could write a procedure that takes the largest type, and explicitly CAST the smaller types to the larger type before passing them. Note that CAST can only be used in SQL
DECLARE
x t_charray_1 := t_charray_1();
y t_charray_2 := t_charray_2();
PROCEDURE foo( p_foo t_charray_2 )
AS
BEGIN
FOR i IN p_foo.FIRST..p_foo.LAST loop
dbms_output.put_line( p_foo(i) );
END LOOP;
END;
BEGIN
x.EXTEND;
x.EXTEND;
x(1) := 'A';
x(2) := 'B';
y.EXTEND;
y.EXTEND;
y(1) := 'AA';
y(2) := 'BB';
foo(y);
SELECT CAST(x AS t_charray_2) INTO y FROM dual;
foo(y);
END;
/
Try:
create or replace function to_chr(p_array in anydata,
pad_length in number,
p_list_length in number) return char as
v_res varchar2(255) := '';
x number;
v_array t_charray_4;
v_array1 t_charray_1;
v_array2 t_charray_2;
v_array3 t_charray_3;
begin
dbms_output.put_line(p_array.GetTypeName);
case p_array.GetTypeName
when '<schema>.T_CHARRAY_1' then
x := p_array.GetCollection(v_array1);
select cast(v_array1 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_2' then
x := p_array.GetCollection(v_array2);
select cast(v_array2 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_3' then
x := p_array.GetCollection(v_array3);
select cast(v_array3 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_4' then
x := p_array.GetCollection(v_array);
end case;
for i in 1 .. p_list_length loop
if v_array is not null and v_array.exists(i) and v_array(i) is not null then
v_res := v_res || rpad(v_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
you can run it like this:
declare
p_array anydata;
v_array t_charray_3 := new t_charray_3('aaa', 'bbb');
v_res varchar2(255);
begin
p_array := anydata.convertcollection(v_array);
v_res := to_chr(p_array => p_array, pad_length => 2, p_list_length => 3);
end;

Resources