Oracle PL/SQL split by new line - oracle

How to split String by new line or other char
String
ABC|123
xyz|098
To
ABC --- 123
xyz --- 098

Try using regexp_substr function...
select regexp_substr('ABC|123','[^|]+',1,1) part1,
regexp_substr('ABC|123','[^|]+',1,2) part2
from dual
And in case of new line (CR/LF) it would look like this:
`regexp_substr(mystr,'[^('||chr(13)||chr(10)||')]+',1,1) part1,
regexp_substr(mystr,'[^('||chr(13)||chr(10)||')]+',1,2) part2`

Make oracle split function, that return varray. Here V_ARRAY is type
create TYPE V_ARRAY
after that create split function which return V_ARRAY
CREATE OR REPLACE TYPE V_ARRAY AS VARRAY(1000) OF VARCHAR2(4000);
CREATE or REPLACE function split(str varchar2, patt varchar2)
RETURN v_array as
s BINARY_integer;
e BINARY_integer;
i BINARY_integer;
token v_array;
begin
token := v_array();
i :=1;
s := 0;
e := INSTR(str, patt);
while(e > 0) loop
token.extend; // add new varchar2
token(i) := substr(str, s+1, e-s-1);
s := e;
e := INSTR(str, patt, s+1);
i := i + 1;
end loop;
token.extend;
token(i) := substr(str, s+1, length(str) -1);
return token;
end;
Example
declare
str varchar(10000);
line varchar(10000);
lines v_array;
subs v_array;
begin
str := 'ABCe123';--|| chr(10) ||'xyz|098';
lines := split(str, chr(10));
--DBMS_OUTPUT.PUT_LINE(lines.count);
for i in 1.. lines.count loop
line := lines(i);
-- DBMS_OUTPUT.PUT_LINE( 'line :' || line);
subs := split(line, '|');
-- DBMS_OUTPUT.PUT_LINE(subs.count);
DBMS_OUTPUT.PUT_LINE( subs(1) || '---' || subs(1) );
end loop;
end;
ref:A function to split, loop through

Related

Currently Executing Procedure Name within the Package

Is there a way to get the currently executing procedure name within the package?
create or replace package test_pkg
as
procedure proc1;
end test_pkg;
create or replace package body test_pkg
as
procedure proc1
is
-- // Get the Procedure Name here?? //
end proc1;
end test_pkg;
In 12c, the current subprogram name is just:
utl_call_stack.subprogram(1)(2);
The current package can also be got from
utl_call_stack.subprogram(1)(1);
but it is generally easier to just use $$plsql_unit. You can also get the qualified name (package.procedure) as:
utl_call_stack.concatenate_subprogram(utl_call_stack.subprogram(1));
However, I can't think of any situation where a procedure or function (or object method) would want its own name. This functionality is mostly useful in a logging procedure, in which case the 'who called me?' code should be in the logger, and not repeated in every single thing that calls it. Therefore, I would strongly suggest avoiding any 'who am I?' logic in procedures. Instead, put something like this in your logger (requires 12.1 or later):
create or replace procedure logdemo
as
k_calling_package constant varchar2(128) := utl_call_stack.subprogram(2)(1);
k_calling_subprog constant varchar2(128) := utl_call_stack.subprogram(2)(2);
begin
dbms_output.put_line
( $$plsql_unit ||
' called from package '||k_calling_package||', subprogram '||k_calling_subprog );
end logdemo;
Unfortunately it's a little more complicated in 11g as you have to parse dbms_utility.format_call_stack, and as this only gives you the package name and line number (in a linefeed-delimited text string), you then have to query all_source to find the subprogram name.
I can post some 11g code if you clarify what it's for. In my 11g logger I found it useful to capture dbms_utility.format_error_backtrace as well as dbms_utility.format_call_stack depending on sqlcode etc, so there is a bunch of logic that is specific to logging, which you may not need if you just want to capture the current procedure name for some other reason.
If it were a standalone procedure (i.e. not the one within the package), you'd get the answer rather easy:
SQL> create or replace procedure p_test is
2 begin
3 dbms_output.put_line('I am ' || $$plsql_unit);
4 null;
5 end;
6 /
Procedure created.
SQL> exec p_test
I am P_TEST
PL/SQL procedure successfully completed.
SQL>
For packages, things aren't that simple any more. The above option (using $$plsql_unit) returns the package body name, not the procedure's name:
SQL> create or replace package pkg_test as
2 procedure p_test_in_pkg;
3 end;
4 /
Package created.
SQL> create or replace package body pkg_test as
2 procedure p_test_in_pkg is
3 begin
4 dbms_output.put_line('Packaged procedure whose name is ' || $$plsql_unit);
5 null;
6 end;
7 end;
8 /
Package body created.
SQL> exec pkg_test.p_test_in_pkg;
Packaged procedure whose name is PKG_TEST --> should have been "p_test_in_pkg"
PL/SQL procedure successfully completed.
The following function seems to be working. The author is garbuya, source is OTN Forum's discussion Efficient WHO_AM_I and WHO_CALLED_ME discussion (dated in 2010).
create or replace
FUNCTION FN_WHO_AM_I ( p_lvl NUMBER DEFAULT 0) RETURN VARCHAR2
IS
/***********************************************************************************************
FN_WHO_AM_I returns the full ORACLE name of your object including schema and package names
--
FN_WHO_AM_I(0) - returns the name of your object
FN_WHO_AM_I(1) - returns the name of calling object
FN_WHO_AM_I(2) - returns the name of object, who called calling object
etc., etc., etc.... Up to to he highest level
-------------------------------------------------------------------------------------------------
Copyrigth GARBUYA 2010
*************************************************************************************************/
TYPE str_varr_t IS VARRAY(2) OF CHAR(1);
TYPE str_table_t IS TABLE OF VARCHAR2(256);
TYPE num_table_t IS TABLE OF NUMBER;
v_stack VARCHAR2(2048) DEFAULT UPPER(dbms_utility.format_call_stack);
v_tmp_1 VARCHAR2(1024);
v_tmp_2 VARCHAR2(1024);
v_pkg_name VARCHAR2(32);
v_obj_type VARCHAR2(32);
v_owner VARCHAR2(32);
v_idx NUMBER := 0;
v_pos1 NUMBER := 0;
v_pos2 NUMBER := 0;
v_line_nbr NUMBER := 0;
v_blk_cnt NUMBER := 0;
v_str_len NUMBER := 0;
v_bgn_cnt NUMBER := 0;
v_end_cnt NUMBER := 0;
it_is_comment BOOLEAN := FALSE;
it_is_literal BOOLEAN := FALSE;
v_literal_arr str_varr_t := str_varr_t ('''', '"');
v_blk_bgn_tbl str_table_t := str_table_t (' IF ' , ' LOOP ' , ' CASE ', ' BEGIN ');
v_tbl str_table_t := str_table_t();
v_blk_bgn_len_tbl num_table_t := num_table_t();
BEGIN
v_stack := SUBSTR(v_stack,INSTR(v_stack,CHR(10),INSTR(v_stack,'FN_WHO_AM_I'))+1)||'ORACLE'; -- skip myself
FOR v_pos2 in 1 .. p_lvl LOOP -- advance to the input level
v_pos1 := INSTR(v_stack, CHR(10));
v_stack := SUBSTR(v_stack, INSTR(v_stack, CHR(10)) + 1);
END LOOP;
v_pos1 := INSTR(v_stack, CHR(10));
IF v_pos1 = 0 THEN
RETURN (v_stack);
END IF;
v_stack := SUBSTR(v_stack, 1, v_pos1 - 1); -- get only current level
v_stack := TRIM(SUBSTR(v_stack, instr(v_stack, ' '))); -- cut object handle
v_line_nbr := TO_NUMBER(SUBSTR(v_stack, 1, instr(v_stack, ' ') - 1)); -- get line number
v_stack := TRIM(SUBSTR(v_stack, instr(v_stack, ' '))); -- cut line number
v_pos1 := INSTR(v_stack, ' BODY');
IF v_pos1 = 0 THEN
RETURN (v_stack);
END IF;
v_pos1 := INSTR(v_stack, ' ', v_pos1 + 2); -- find end of object type
v_obj_type := SUBSTR(v_stack, 1, v_pos1 - 1); -- get object type
v_stack := TRIM(SUBSTR(v_stack, v_pos1 + 1)); -- get package name
v_pos1 := INSTR(v_stack, '.');
v_owner := SUBSTR(v_stack, 1, v_pos1 - 1); -- get owner
v_pkg_name := SUBSTR(v_stack, v_pos1 + 1); -- get package name
v_blk_cnt := 0;
it_is_literal := FALSE;
--
FOR v_idx in v_blk_bgn_tbl.FIRST .. v_blk_bgn_tbl.LAST
LOOP
v_blk_bgn_len_tbl.EXTEND(1);
v_blk_bgn_len_tbl (v_blk_bgn_len_tbl.last) := LENGTH(v_blk_bgn_tbl(v_idx));
END LOOP;
--
FOR src
IN ( SELECT ' '||REPLACE(TRANSLATE(UPPER(text), ';('||CHR(10), ' '),'''''',' ') ||' ' text
FROM all_source
where owner = v_owner
and name = v_pkg_name
and type = v_obj_type
and line < v_line_nbr
ORDER BY line
)
LOOP
v_stack := src.text;
IF it_is_comment THEN
v_pos1 := INSTR (v_stack, '*/');
IF v_pos1 > 0 THEN
v_stack := SUBSTR (v_stack, v_pos1 + 2);
it_is_comment := FALSE;
ELSE
v_stack := ' ';
END IF;
END IF;
--
IF v_stack != ' ' THEN
--
v_pos1 := INSTR (v_stack, '/*');
WHILE v_pos1 > 0 LOOP
v_tmp_1 := SUBSTR (v_stack, 1, v_pos1 - 1);
v_pos2 := INSTR (v_stack, '*/');
IF v_pos2 > 0 THEN
v_tmp_2 := SUBSTR (v_stack, v_pos2 + 2);
v_stack := v_tmp_1||v_tmp_2;
ELSE
v_stack := v_tmp_1;
it_is_comment := TRUE;
END IF;
v_pos1 := INSTR (v_stack, '/*');
END LOOP;
--
IF v_stack != ' ' THEN
v_pos1 := INSTR (v_stack, '--');
IF v_pos1 > 0 THEN
v_stack := SUBSTR (v_stack, 1, v_pos1 - 1);
END IF;
--
IF v_stack != ' ' THEN
FOR v_idx in v_literal_arr.FIRST .. v_literal_arr.LAST
LOOP
v_pos1 := INSTR (v_stack, v_literal_arr (v_idx) );
WHILE v_pos1 > 0 LOOP
v_pos2 := INSTR (v_stack, v_literal_arr (v_idx), v_pos1 + 1);
IF v_pos2 > 0 THEN
v_tmp_1 := SUBSTR (v_stack, 1, v_pos1 - 1);
v_tmp_2 := SUBSTR (v_stack, v_pos2 + 1);
v_stack := v_tmp_1||v_tmp_2;
ELSE
IF it_is_literal THEN
v_stack := SUBSTR (v_stack, v_pos1 + 1);
it_is_literal := FALSE;
ELSE
v_stack := SUBSTR (v_stack, 1, v_pos1 - 1);
it_is_literal := TRUE;
END IF;
END IF;
v_pos1 := INSTR (v_stack, v_literal_arr (v_idx) );
END LOOP;
END LOOP;
--
IF v_stack != ' ' THEN
WHILE INSTR (v_stack, ' ') > 0
LOOP
v_stack := REPLACE(v_stack, ' ', ' ');
END LOOP;
v_stack := REPLACE(v_stack, ' END IF ', ' END ');
v_stack := REPLACE(v_stack, ' END LOOP ', ' END ');
--
IF v_stack != ' ' THEN
v_stack := ' '||v_stack;
v_pos1 := INSTR(v_stack, ' FUNCTION ') + INSTR(v_stack, ' PROCEDURE ');
IF v_pos1 > 0 THEN
v_obj_type := TRIM(SUBSTR(v_stack, v_pos1 + 1, 9)); -- get object type
v_stack := TRIM(SUBSTR(v_stack, v_pos1 + 10))||' '; -- cut object type
v_stack := SUBSTR(v_stack, 1, INSTR(v_stack, ' ') - 1 ); -- get object name
v_tbl.EXTEND(1);
v_tbl (v_tbl.last) := v_obj_type||' '||v_owner||'.'||v_pkg_name||'.'||v_stack;
END IF;
--
v_pos1 := 0;
v_pos2 := 0;
v_tmp_1 := v_stack;
v_tmp_2 := v_stack;
FOR v_idx in v_blk_bgn_tbl.FIRST .. v_blk_bgn_tbl.LAST
LOOP
v_str_len := NVL(LENGTH(v_tmp_1),0);
v_tmp_1 := REPLACE(v_tmp_1,v_blk_bgn_tbl(v_idx), NULL);
v_bgn_cnt := NVL(LENGTH(v_tmp_1), 0);
v_pos1 := v_pos1 + (v_str_len - v_bgn_cnt)/v_blk_bgn_len_tbl(v_idx);
v_str_len := NVL(LENGTH(v_tmp_2),0);
v_tmp_2 := REPLACE(v_tmp_2,' END ', NULL);
v_end_cnt := NVL(LENGTH(v_tmp_2), 0);
v_pos2 := v_pos2 + (v_str_len - v_end_cnt)/5; --- 5 is the length(' END ')
END LOOP;
IF v_pos1 > v_pos2 THEN
v_blk_cnt := v_blk_cnt + 1;
ELSIF v_pos1 < v_pos2 THEN
v_blk_cnt := v_blk_cnt - 1;
IF v_blk_cnt = 0 AND v_tbl.COUNT > 0 THEN
v_tbl.DELETE(v_tbl.last);
END IF;
END IF;
END IF;
END IF;
END IF;
END IF;
END IF;
END LOOP;
RETURN CASE v_tbl.COUNT WHEN 0 THEN 'UNKNOWN' ELSE v_tbl(v_tbl.LAST) END;
END;
/
Testing:
SQL> create or replace package body pkg_test as
2 procedure p_test_in_pkg is
3 begin
4 dbms_output.put_line('Packaged procedure, using $$plsql_unit = ' || $$plsql_unit);
5 dbms_output.put_line('FN_WHO_AM_I = ' || fn_who_am_i);
6 null;
7 end;
8 end;
9 /
Package body created.
SQL> exec pkg_test.p_test_in_pkg;
Packaged procedure, using $$plsql_unit = PKG_TEST --> this one is wrong (package body name) ...
FN_WHO_AM_I = PROCEDURE SCOTT.PKG_TEST.P_TEST_IN_PKG --> ... but this one is OK
PL/SQL procedure successfully completed.
SQL>
An approach using the idea borrowed from here : http://www.orafaq.com/forum/t/173023/
Note : This works fine only in Oracle 12c. For 11g, It only gives Package name.
Create a procedure called show_caller. This uses the OWA_UTIL.WHO_CALLED_ME procedure.
create or replace procedure show_caller
as
l_owner varchar2(200);
l_name varchar2(200);
l_lineno number;
l_caller varchar2(200);
begin
OWA_UTIL.WHO_CALLED_ME (l_owner, l_name,l_lineno,l_caller);
dbms_output.put_line('Inside '||l_name);
end;
/
Now, you can write your package as:
CREATE OR replace PACKAGE test_pkg
AS
PROCEDURE proc1;
END test_pkg;
/
CREATE OR replace PACKAGE BODY test_pkg
AS
PROCEDURE Proc1
AS
BEGIN
show_caller; -- proc1 calls show_caller
END proc1;
END test_pkg;
/
Execution.
SET SERVEROUTPUT ON
BEGIN
Test_pkg.Proc1;
END;
/
Inside TEST_PKG.PROC1
Note that this will print the name of the procedure.If you want to use it as a variable, pass l_name(along with others if needed) as OUT variable from show_caller
Live SQL Demo ( Free OTN account required )
One other option is to use OWA_UTIL.GET_PROCEDURE function within the procedure: but it doesn't seem work for me in this context. I would be glad to know more about this from experts.
My quick sample:
create or replace package body MyPackage as
procedure PrintMethodName is
ThisMethod#FullName varchar2(128) := regexp_substr(
dbms_utility.format_call_stack,
'package\s+body\s+([^[:space:]]+)',
1,1,'i',1); -- schema.package.method
ThisMethod#CurrentSchemaName varchar2(128) := regexp_substr(
regexp_substr(
dbms_utility.format_call_stack,
'package\s+body\s+([^[:space:]]+)',
1,1,'i',1),
'[^.]+[.][^.]+$'); -- package.method
ThisMethod#MethodName varchar2(128) := regexp_substr(
regexp_substr(
dbms_utility.format_call_stack,
'package\s+body\s+([^[:space:]]+)',
1,1,'i',1),
'[^.]+$'); -- method
begin
dbms_output.put_line(dbms_utility.format_call_stack);
dbms_output.put_line(ThisMethod#FullName);
dbms_output.put_line(ThisMethod#CurrentSchemaName);
dbms_output.put_line(ThisMethod#MethodName);
end;
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;

Can I iterate over columns of a composite type?

Let's say I have the following table named bar:
key | columnA | columnB | columnC
A | B | C | D
E | F | G | H
I want to write a function taking a key and a string and doing the following (best described by examples):
Input: ('A', '${columnB} - ${columnA}') / Output : 'C - B'
Input: ('B', 'Hello ${columnC}') / Output: 'Hello H'
For the moment, I have this implementation:
CREATE OR REPLACE FUNCTION foo
( param_key IN VARCHAR2
, format_string IN VARCHAR2
)
RETURN VARCHAR2
IS
my_row bar%ROWTYPE;
retval VARCHAR2(4000);
BEGIN
BEGIN SELECT * INTO my_row FROM bar WHERE "key" = param_key;
EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL;
END;
retval := format_string;
retval := REPLACE(retval, '${columnA}', my_row.columnA);
retval := REPLACE(retval, '${columnB}', my_row.columnB);
retval := REPLACE(retval, '${columnC}', my_row.columnC);
RETURN retval;
END;
/
I would like to avoid enumerating all columns one by one in the last part, because the structure of my table can change (new columns for instance). Is there a way to iterate on all columns of my_row, and to replace ${the column name} with the value stored in that column, in a generic way?
Thank you
Another way to achieve this.
Create xmltype from table row.
Create xsl-transform from format_string.
Transform xml using xsl
declare
v_string_format varchar2(200) := '{columnA} + {columnB} + {columnA}{columnB}';
v_key varchar2(10) := 'A';
v_cursor sys_refcursor;
l_xml xmltype;
v_xslt VARCHAR2(500):='<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/ROWSET/ROW">{patern}</xsl:template></xsl:stylesheet>';
begin
-- create xsl transform
v_string_format := upper(v_string_format);
v_string_format := REPLACE(v_string_format,'{','<xsl:value-of select="');
v_string_format := REPLACE(v_string_format,'}','"/>');
v_xslt := replace(v_xslt,'{patern}',v_string_format);
dbms_output.put_line(v_string_format);
-- open cursor for table
open v_cursor for select * from bar where key = v_key;
-- get v_cursor as xmltype.
l_xml := xmltype(v_cursor);
-- print xml
dbms_output.put_line(l_xml.getClobVal());
-- tranform xml and print result
dbms_output.put_line(l_xml.transform(xmltype(v_xslt)).getClobVal());
close v_cursor;
end;
A more efficient solution is this one. For sure you have to write more code and it uses the full scope of dynamic SQL.
CREATE OR REPLACE FUNCTION foo (param_key IN VARCHAR2, format_string IN VARCHAR2)
RETURN VARCHAR2 IS
retval VARCHAR2(4000) := format_string;
cur SYS_REFCURSOR;
curId INTEGER;
descTab DBMS_SQL.DESC_TAB;
colCnt NUMBER;
numvar NUMBER;
datevar DATE;
namevar VARCHAR2(4000);
tsvar TIMESTAMP;
BEGIN
OPEN cur FOR SELECT * FROM bar WHERE "key" = param_key;
curId := DBMS_SQL.TO_CURSOR_NUMBER(cur);
DBMS_SQL.DESCRIBE_COLUMNS(curId, colCnt, descTab);
-- Define columns
FOR i IN 1..colcnt LOOP
IF desctab(i).col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, numvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_DATE THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, datevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_TIMESTAMP THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, tsvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
DBMS_SQL.DEFINE_COLUMN(curid, i, namevar, 4000);
--ELSIF desctab(i).col_type = ... THEN
--DBMS_SQL.DEFINE_COLUMN(curid, i, ...);
END IF;
END LOOP;
-- Fetch Rows
IF DBMS_SQL.FETCH_ROWS(curid) > 0 THEN
-- Fetch only the first row and do not consider if further rows exist,
-- otherwise use WHILE DBMS_SQL.FETCH_ROWS(curid) > 0 LOOP
FOR i IN 1..colcnt LOOP
IF desctab(i).col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
DBMS_SQL.COLUMN_VALUE(curid, i, namevar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', namevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
DBMS_SQL.COLUMN_VALUE(curid, i, numvar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', numvar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_DATE THEN
DBMS_SQL.COLUMN_VALUE(curid, i, datevar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', datevar);
ELSIF desctab(i).col_type = DBMS_TYPES.TYPECODE_TIMESTAMP THEN
DBMS_SQL.COLUMN_VALUE(curid, i, tsvar);
retval := REPLACE(retval, '${'||desctab(i).col_name||'}', tsvar);
--ELSIF desctab(i).col_type = ... THEN
--DBMS_SQL.COLUMN_VALUE(curid, i, ...);
--retval := REPLACE(retval, '${'||desctab(i).col_name||'}', ...);
END IF;
END LOOP;
ELSE
retval := NULL;
END IF;
DBMS_SQL.CLOSE_CURSOR(curId);
RETURN retval;
END;
You can get the result you are after using dynamic queries...
CREATE OR REPLACE FUNCTION foo
( param_key IN VARCHAR2
, format_string IN VARCHAR2
)
RETURN VARCHAR2
IS
retval VARCHAR2(4000) := format_string;
cols SYS.ODCIVARCHAR2LIST;
BEGIN
SELECT COLUMN_NAME
BULK COLLECT INTO cols
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'bar'
ORDER BY COLUMN_ID;
FOR i IN 1 .. cols.COUNT LOOP
EXECUTE IMMEDIATE 'SELECT REPLACE( :1, ''${' || cols(i) || '}'', ' || cols(i) || ' ) FROM bar WHERE key = :2'
INTO retval
USING retval, param_key;
END LOOP;
RETURN retval;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END;
/
... but:
This uses dynamic SQL to query the table directly and does not use a %ROWTYPE record.
You may not have access to USER_TAB_COLUMNS (or may need ALL_TAB_COLUMNS) and the DBA might not want you to have access to the data dictionary tables.
It is probably (almost certainly) very inefficient.
I've seen this done before and never let it pass a code review (writing out the explicit column names has always seemed preferable).
So, while it is possible, I would say don't do this.

oracle 8 extract strings from string

I have
"This is <>sample<> text. Need to extract <>smth1<> and <>smth2<> and many others."
in a column of oracle database.
I need to get:
sample
smth1
smth2
Any help?
try to create this function:
create or replace
Function GetSurroundedText (origStr varchar2, openingStr varchar2, closingStr varchar2, outputSep varchar2)
Return Varchar2
Is
continue boolean := true;
l_string Varchar2(2000) := origStr;
startPos PLS_Integer;
endPos PLS_Integer;
openingStrPos PLS_Integer;
res Varchar2(2000) := '';
sep Varchar2(100) := outputSep;
Begin
While true
Loop
openingStrPos :=Instr(l_string, openingStr);
If openingStrPos > 0 Then
startPos := openingStrPos + Length(openingStr);
l_String := Substr(l_string, startPos);
else
exit;
end if;
endPos := Instr(l_string, closingStr);
if endPos > 0 Then
if res = '' Then
sep := '';
else
sep := outputSep;
end If;
res := res || sep || Substr(l_string, 1, endpos-1);
l_String := Substr(l_string, endPos + Length(closingStr));
else
exit;
end if;
End Loop;
return res;
End;
And, in your case, use it like this:
select GetSurroundedText(mycolumn, '<>', '<>', ' ') from mytable;
In Oracle/PLSQL, the replace function replaces a sequence of characters in a string with another set of characters.
replace( string1, string_to_replace, [ replacement_string ])
so
replace( YourString, '<>', '');
Should do the trick.
If your case is more complex and you need a more elaborated solution you can check this function that allow you extract words between delimiters.
http://www.oradev.com/parse_string.jsp
Hope it helps.
Use replace function to replace the <> to ''.It will work

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