Oracle PL/SQL split csv string into n parts - oracle

I need to split a csv string into n parts. I have addresses in various formats, so I converted them to a csv string. My report requires 3 or 4 address line fields, so I want to split my csv string into the number of parts the report requires.
For example:
10,Dingle Apartments,MANZINI,,MANZINI,NGWANE STREET,,,Swaziland
should be split into equal parts for 3 fields:
1:10,Dingle Apartments,MANZINI
2:,MANZINI,NGWANE STREET
3:,,,Swaziland
I wrote the following code, but it does not work well at all:
-- ============================================================================
-- This function splits a csv string into x parts and returns one of the parts.
-- Note: The smallest number of items in a part is hard coded to 2. Needs debug.
--
-- p_string - The csv string.
-- p_parts - The number of parts to split into.
-- p_part - The splitted part to return.
-- p_separator - The separator used in the csv string.
-- p_separator_out - The separator to return.
-- p_trim - Trim trailing separator. Y/N
-- ============================================================================
function get_csv_part
(
p_string in varchar2,
p_parts in number,
p_part in number,
p_separator in varchar2,
p_separator_out in varchar2,
p_trim in varchar2 default 'Y'
) return varchar2 is
l_answer varchar2(32767) := '';
l_count number := 0;
l_count2 number := 0;
l_size number;
l_pos number;
l_pos2 number;
begin
-- hr_utility.trace_on(null, 'FDL');
hr_utility.trace('p_string: ' || p_string);
l_pos := instr(p_string, p_separator);
-- Determine the number of separators.
while l_pos > 0 loop
l_count := l_count + 1;
l_pos := instr(p_string, p_separator, l_pos + 1);
end loop;
-- Get the size of a part.
if l_count <= p_parts then
l_size := 2;
else
l_size := floor(l_count / p_parts);
end if;
--if l_size = 1 then
-- l_size := 2;
--end if;
hr_utility.trace('l_size: ' || to_char(l_size));
l_pos := instr(p_string, p_separator);
if l_pos = 0 then
if p_part = 1 then
l_answer := p_string;
end if;
else
if p_part = 1 then
l_answer := substr(p_string, 1, l_pos - 1) || p_separator_out;
end if;
end if;
-- Split csv into parts.
while l_pos > 0 loop
l_count2 := l_count2 + 1;
l_pos2 := instr(p_string, p_separator, l_pos + 1);
hr_utility.trace('----------------------------------------');
hr_utility.trace('l_count: ' || to_char(l_count));
hr_utility.trace('l_count2: ' || to_char(l_count2));
hr_utility.trace('floor(l_count2 / l_size) + 1: ' || to_char(floor(l_count2 / l_size) + 1));
hr_utility.trace('l_pos: ' || to_char(l_pos));
hr_utility.trace('l_pos2: ' || to_char(l_pos2));
hr_utility.trace('l_answer: ' || l_answer);
-- If we are at a position that should go into the returned part.
if
(
l_size > 1
and floor(l_count2 / l_size) + 1 = p_part
)
or
(
l_size = 1
and l_count2 = p_part
)
or
(
p_part = p_parts
and l_size = 1
and l_count2 >= p_part
)
or
(
p_part = p_parts
and floor(l_count2 / l_size) + 1 >= p_part
) then
if l_pos2 = 0 then
if l_pos + 1 < length(p_string) then
l_answer := l_answer || substr(p_string, l_pos + 1) || p_separator_out;
end if;
elsif ((l_pos + 1) <= (l_pos2 - 1)) then
l_answer := l_answer || substr(p_string, l_pos + 1, ((l_pos2 - 1) - (l_pos + 1) + 1)) || p_separator_out;
else
l_answer := l_answer || p_separator_out;
end if;
end if;
l_pos := l_pos2;
end loop;
if p_part = p_parts then
l_pos := instr(p_string, p_separator, 1, l_count);
-- Dodge.
if instr(p_string, substr(p_string, l_pos + 1)) = 0 then
l_answer := l_answer || substr(p_string, l_pos + 1);
end if;
end if;
if p_trim = 'Y' then
-- Did not work if all separators.
-- l_answer := trim(trailing p_separator_out from l_answer);
if substr(l_answer, length(l_answer)) = p_separator_out then
l_answer := substr(l_answer, 1, length(l_answer) - 1);
end if;
end if;
return l_answer;
end get_csv_part;
Some of the issues are:
It does not work for part size of 1
It puts address parts into multiple parts, for example ,,TEST,SWAZILIND,LOCATION,THING,,,,
It skips address parts
It uses unequal part sizes.
Note that for some reason the requirement is to leave the blank fields with separators so a part could be ,
Can anyone help me fix this or does anyone have a function that can do this?

Untested but something like:
CREATE OR REPLACE function get_csv_part(
p_string in varchar2,
p_parts in number,
p_part in number,
p_separator in varchar2,
p_separator_out in varchar2,
p_trim in varchar2 default 'Y'
)
RETURN VARCHAR2 DETERMINISTIC
IS
p_value VARCHAR2(4000) := p_string;
p_start INTEGER;
p_end INTEGER;
p_items INTEGER;
p_sep_len CONSTANT INTEGER := LENGTH( p_separator );
BEGIN
IF p_value IS NULL THEN
RETURN NULL;
END IF;
p_items = ( LENGTH( p_value ) - LENGTH( REPLACE( p_value, p_separator ) ) ) / p_sep_len + 1;
IF p_part = 1 THEN
p_start := 1;
ELSE
p_start := INSTR( p_value, p_separator, 1, ROUND( ( p_part - 1 ) / p_parts * p_items ) ) + p_sep_len;
END IF;
p_end := INSTR( p_value, p_separator, 1, ROUND( p_part / p_parts * p_items ) ) - 1;
IF p_end = -1 THEN
p_value := SUBSTR( p_value, p_start );
ELSE
p_value := SUBSTR( p_value, p_start, p_end - p_start + 1 );
END IF;
IF p_trim = 'Y' THEN
WHILE SUBSTR( p_value, -p_sep_len ) = p_separator THEN
p_value := SUBSTR( p_value, 1, LENGTH( p_value ) - p_sep_len );
END LOOP;
END IF;
RETURN REPLACE( p_value, p_separator, p_separator_out );
END;
/

Related

Is it possible to write function and procedure within single program in PL/SQL without using packages?

I'm trying to calculate LCM using GCF but somehow i'm getting error saying "no function with name LCM exists in the scope". What can i do about this?. I think this error is because i'm writing procedure and function together..
create or replace FUNCTION gcf (
x IN INTEGER,
y IN INTEGER
) RETURN INTEGER IS
res INTEGER;
BEGIN
IF ( y = 0 ) OR MOD(y,x) = 0 THEN
RETURN x;
ELSIF ( x = 0 ) THEN
RETURN y;
ELSIF ( x < y ) THEN
res := gcf(y,x);
ELSE
res := gcf(y,MOD(x,y) );
END IF;
RETURN res;
END;
/
create or replace PROCEDURE lcm (
num1 IN INTEGER,
num2 IN INTEGER,
answer OUT INTEGER
) IS
BEGIN
IF num1 = 0 AND num2 = 0 THEN
answer := 0;
ELSE
answer := abs(num1 * num2) / gcf(num1,num2);
END IF;
END lcm;
/
DECLARE
c integer;
BEGIN
dbms_output.put_line(' LCM(8, 12)-> ' || lcm(8, 12,c) );
dbms_output.put_line(' LCM(38,150)-> ' || lcm(38,150,c) );
dbms_output.put_line(' LCM(16,50)-> ' || lcm(16,60,c) );
dbms_output.put_line(' LCM(16,60)-> ' || lcm(16,60,c) );
dbms_output.put_line(' LCM(48,99)-> ' || lcm(48,99,c) );
END;
/
You can't use a PL/SQL procedure like a function. You must simply run it without being part of any other expression, which will set the value of the out parameter c.
DECLARE
c integer;
BEGIN
lcm(8, 12,c);
dbms_output.put_line(' LCM(8, 12)-> ' || c);
lcm(38,150,c);
dbms_output.put_line(' LCM(38,150)->' || c);
lcm(16,60,c);
dbms_output.put_line(' LCM(16,50)-> ' || c);
lcm(16,60,c);
dbms_output.put_line(' LCM(16,60)-> ' || c);
lcm(48,99,c);
dbms_output.put_line(' LCM(48,99)-> ' || c );
END;
/
Create the procedure and function inside the procedure as follows:
create or replace PROCEDURE lcm (
num1 IN INTEGER,
num2 IN INTEGER,
answer OUT INTEGER
) IS
FUNCTION gcf (
x IN INTEGER,
y IN INTEGER
) RETURN INTEGER IS
res INTEGER;
BEGIN
IF ( y = 0 ) OR MOD(y,x) = 0 THEN
RETURN x;
ELSIF ( x = 0 ) THEN
RETURN y;
ELSIF ( x < y ) THEN
res := gcf(y,x);
ELSE
res := gcf(y,MOD(x,y) );
END IF;
RETURN res;
END;
BEGIN
IF num1 = 0 AND num2 = 0 THEN
answer := 0;
ELSE
answer := abs(num1 * num2) / gcf(num1,num2);
END IF;
END lcm;
/
Then call it:
DECLARE
c INTEGER;
BEGIN
lcm (8, 12, c);
DBMS_OUTPUT.put_line (' LCM(8, 12)-> ' || c);
lcm (38, 150, c);
DBMS_OUTPUT.put_line (' LCM(38,150)-> ' || c);
lcm (16, 60, c);
DBMS_OUTPUT.put_line (' LCM(16,50)-> ' || c);
lcm (16, 60, c);
DBMS_OUTPUT.put_line (' LCM(16,60)-> ' || c);
lcm (48, 99, c);
DBMS_OUTPUT.put_line (' LCM(48,99)-> ' || c);
END;
The Lowest Common Multiple should be a function (since it returns a single value):
CREATE FUNCTION lcm (
num1 IN INTEGER,
num2 IN INTEGER,
) RETURN INTEGER DETERMINISTIC
IS
BEGIN
IF num1 = 0 AND num2 = 0 THEN
RETURN 0;
END IF;
RETURN abs(num1 * num2) / gcf(num1,num2);
END lcm;
/
Then you can use the code:
BEGIN
dbms_output.put_line(' LCM(8, 12)-> ' || lcm(8, 12) );
dbms_output.put_line(' LCM(38,150)-> ' || lcm(38,150) );
dbms_output.put_line(' LCM(16,50)-> ' || lcm(16,60) );
dbms_output.put_line(' LCM(16,60)-> ' || lcm(16,60) );
dbms_output.put_line(' LCM(48,99)-> ' || lcm(48,99) );
END;
/

How write a PL/SQL program that prints out string which looking like xml format

Input String : “a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k3g5g5k3w2”
I tried this code as first step:
declare
word varchar2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num number := length(word)/2;
name_array dbms_sql.varchar2_table;
begin
dbms_output.put_line(word);
FOR i IN 1..num LOOP
name_array(i) := substr(word, -2*i, 2);
END LOOP;
FOR i IN name_array.FIRST .. name_array.LAST LOOP
dbms_output.put_line(name_array(i));
END LOOP;
end;
This code creates only an array of string. Not xml format. I need this output:
Which SQL functions,conditional clauses... do I need to use?
Oracle Setup:
CREATE OR REPLACE TYPE CHARS_TABLE IS TABLE OF CHAR(2);
/
CREATE OR REPLACE TYPE INTEGERS_TABLE IS TABLE OF INTEGER;
/
PL/SQL:
This assumes a well-formed set of character pairs and just indents each pair to the appropriate level:
DECLARE
word VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth_array INTEGERS_TABLE := INTEGERS_TABLE();
open_array INTEGERS_TABLE := INTEGERS_TABLE();
BEGIN
name_array.EXTEND( num );
depth_array.EXTEND( num );
open_array.EXTEND( num );
name_array(1) := SUBSTR( word, 1, 2 );
depth_array(1) := 1;
open_array(1) := 1;
FOR i IN 2 .. num LOOP
name_array(i) := SUBSTR( word, 2*i - 1, 2 );
open_array(i) := 1;
FOR j IN 1 .. i-1 LOOP
IF name_array(j) = name_array(i) THEN
open_array(i) := -open_array(i);
END IF;
END LOOP;
depth_array(i) := depth_array(i-1) + open_array(i);
END LOOP;
FOR i IN 1 .. num LOOP
FOR j IN 2 .. depth_array(i) + CASE open_array(i) WHEN 1 THEN 0 ELSE 1 END LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name_array(i) );
END LOOP;
END;
/
Output:
a4
b4
c2
d9
d9
c2
e6
e6
b4
s2
o1
o1
s2
a4
w2
r8
r8
k2
g5
g5
k2
w2
Update - Simpler Stack-Based Version:
DECLARE
word CONSTANT VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num CONSTANT PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth PLS_INTEGER := 0;
name CHAR(2);
PROCEDURE indent( depth PLS_INTEGER, name CHAR )
IS
BEGIN
FOR j IN 2 .. depth LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name );
END;
BEGIN
name_array.EXTEND( num );
FOR i IN 1 .. num LOOP
name := SUBSTR( word, 2*i - 1, 2 );
IF depth > 0 AND name = name_array(depth) THEN
indent(depth,name);
depth := depth - 1;
ELSE
depth := depth - 1;
name_array(depth) := name;
indent(depth,name);
END IF;
END LOOP;
END;
/
DECLARE
vs_CurrentChar VARCHAR2(1);
vs_NextChar VARCHAR2(1);
vs_TempText VARCHAR2(100);
vs_InputText VARCHAR2(100) := 'abcdffdcba';
vn_LengthOfText NUMBER := 1;
vn_WhileIndex NUMBER := 1;
vs_Spaces VARCHAR(100);
BEGIN
vs_TempText := NULL;
vs_CurrentChar := substr(vs_InputText, vn_WhileIndex, vn_LengthOfText);
dbms_output.put_line(vs_CurrentChar);
WHILE vn_WhileIndex < length(vs_InputText) - 1 LOOP
vs_NextChar := substr(vs_InputText, vn_WhileIndex + 1, vn_LengthOfText);
EXIT WHEN vs_CurrentChar = vs_NextChar;
vs_TempText := vs_TempText || vs_CurrentChar;
vs_CurrentChar := vs_NextChar;
vs_Spaces := NULL;
FOR i IN 1 .. vn_WhileIndex LOOP
vs_Spaces := vs_Spaces || chr(9); --'*';
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
vn_WhileIndex := vn_WhileIndex + 1;
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
FOR i IN 1 .. length(vs_TempText) LOOP
vs_Spaces := substr(vs_Spaces, vn_LengthOfText, length(vs_Spaces) - 1);
vs_CurrentChar := substr(vs_TempText, -i, vn_LengthOfText);
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
END LOOP;
END;
/
And output:
a
b
c
d
f
f
d
c
b
a
even, if you put '*'; instead of chr(9); then output will look like as:
a
*b
**c
***d
****f
****f
***d
**c
*b
a

Response time of the query is too high in oracle 11g

SELECT /*+first_rows */ PPS_ID,TOTAL_WEIGHT from
(SELECT PPS_ID,TOTAL_WEIGHT ,row_number() over (order by total_weight desc) row_num
FROM (SELECT pps_id,round((((60 * name_pct_match / 100) + prs_weight + year_weight + dt_weight +
case
when mother_name_pct_match = -1
then
0
else
(10 * mother_name_pct_match / 100)
end)/decode(mother_name_pct_match,-1,(total_attrib_weight - mother_weight),total_attrib_weight)) * 100) total_weight
FROM (SELECT pps_id,
round(func_compare_name('MUHAMMAD YASIN MUHAMMAD ASHRAF',upper(name_en),' ',60)) name_pct_match,
decode(prs_nationality_id, 271, 15, 0) prs_weight,
case
when upper(mother_name_en) in ('MR','MRS','MISS','NISA','M','X')
then -1
else
round(func_compare_name(upper('.'), upper(mother_name_en), ' ',60))
end mother_name_pct_match, 10 mother_weight,
100 total_attrib_weight,
case when to_number(to_char(birth_date, 'yyyy')) = 2007 then 5 else 0 end year_weight,
case when to_char(to_date('01/01/2007','DD-MM-RRRR'), 'dd') = to_char(birth_date, 'dd')
and to_char(to_date('01/01/07','DD-MM-RRRR'), 'mm') = to_char(birth_date, 'mm') then 10
when to_date('01/01/2007','DD-MM-RRRR') between birth_date-6 and birth_date+6 then 8
when to_date('01/01/2007','DD-MM-RRRR') between birth_date-28 and birth_date+28 then 5
when to_date('01/01/2007','DD-MM-RRRR') between birth_date-90 and birth_date+90 then 3
else 0
end dt_weight
FROM INDIV_PROF_sub_test123
WHERE birth_date = '01/01/2007'
AND IS_ACTIVE = 1
AND gender_id = 1
AND round(func_compare_name('MUHAMMAD YASIN MUHAMMAD ASHRAF',upper(name_en),' ',60)) > 20
)
)
WHERE TOTAL_WEIGHT >= 70
)
where row_num <= 10
CREATE OR REPLACE FUNCTION VISION_APP.func_compare_name(p_name_str IN VARCHAR2,
p_str IN VARCHAR2,
p_delim IN VARCHAR2,
p_relev_thresh IN NUMBER)
RETURN NUMBER DETERMINISTIC AS
l_str LONG;
l_n NUMBER;
TYPE mytabletype IS TABLE OF VARCHAR2(255);
l_name_data mytabletype := mytabletype();
l_data mytabletype := mytabletype();
v_name_cnt NUMBER := 0;
v_pct_per_name NUMBER := 0;
v_pct_match NUMBER := 0;
v_flag NUMBER;
v_jaro_pct NUMBER := 0;
v_highest_jaro_pct NUMBER := 0;
v_match_exact NUMBER := 0;
v_match_exact_res NUMBER := 0;
BEGIN
l_str := p_name_str || p_delim;
LOOP
l_n := instr(l_str, p_delim);
EXIT WHEN(nvl(l_n, 0) = 0);
-- condition to check if only space
IF l_n <> 1 THEN
l_name_data.extend;
l_name_data(l_name_data.count) := ltrim(rtrim(substr(l_str,
1,
l_n - 1)));
v_name_cnt := v_name_cnt + 1;
END IF;
l_str := substr(l_str, l_n + length(p_delim));
END LOOP;
v_pct_per_name := 100 / v_name_cnt;
l_str := p_str || p_delim;
LOOP
l_n := instr(l_str, p_delim);
EXIT WHEN(nvl(l_n, 0) = 0);
l_data.extend;
l_data(l_data.count) := ltrim(rtrim(substr(l_str, 1, l_n - 1)));
l_str := substr(l_str, l_n + length(p_delim));
END LOOP;
FOR nme IN 1 .. l_name_data.count LOOP
v_flag := 0;
v_highest_jaro_pct := 0;
FOR i IN 1 .. l_data.count LOOP
v_jaro_pct := utl_match.jaro_winkler_similarity(l_name_data(nme),
l_data(i));
IF soundex(l_name_data(nme)) = soundex(l_data(i)) AND
v_jaro_pct >= p_relev_thresh THEN
IF v_jaro_pct > v_highest_jaro_pct THEN
v_highest_jaro_pct := v_jaro_pct;
END IF;
END IF;
END LOOP;
v_pct_match := v_pct_match +
(v_pct_per_name * v_highest_jaro_pct / 100);
END LOOP;
SELECT utl_match.edit_distance_similarity(p_name_str, p_str)
INTO v_match_exact
FROM dual;
if (trunc(v_match_exact) =100 ) then
return trunc(v_pct_match, 2);
else
if( v_match_exact <> 0) then
v_match_exact_res := (5 / v_match_exact) * 100;
v_pct_match := v_pct_match - v_match_exact_res;
end if;
if v_pct_match >20 then
RETURN trunc(v_pct_match, 2);
end if;
end if;
END;
This query is fetching result from INDIV_PROF_sub_test123 table this is having data of 35 million and partitioned.
i found the problematic area in query func_compare_name we are using it having name similarity checking function
Adding explain plan:
Plan
SELECT STATEMENT HINT: FIRST_ROWS
Cost: 262
Bytes: 4,056
Cardinality: 104 4
VIEW VC_CLONE.
Cost: 262
Bytes: 4,056
Cardinality: 104 3
WINDOW SORT PUSHED RANK
Cost: 262
Bytes: 5,512
Cardinality: 104 2
TABLE ACCESS BY GLOBAL INDEX ROWID TABLE VC_CLONE.INDIV_PROF_ONE_MONTH_1
Cost: 261
Bytes: 5,512
Cardinality: 104
Partition #: 3
Partitions accessed #1672 1
INDEX RANGE SCAN INDEX VC_CLONE.IDX_BIRTH_DT_INVID
Cost: 4
Cardinality: 1 –

PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: begin function pragma procedure

i know the same answer is asked before, but i'm just staring blind on my code.
what's wrong with my function???
other posts say it's missing a ; but i just can't find it.
FUNCTION checkIBAN
( p_IBAN in varchar2 )
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2;
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2;
BEGIN
v_landcode := SUBSTRING(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if p_IBAN.LENGTH != v_lengte
then return 'F';
end if;
v_omgezettelandcode := SUBSTRING(p_IBAN, 5) || SUBSTRING(p_IBAN, 1, 4);
WHILE v_teller < v_omgezettelandcode.LENGTH LOOP
select getal
into SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte)
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
v_teller := v_teller + 1;
END LOOP;
d := v_omgezettelandcode;
n := SUBSTRING(d, 1, 9);
d := SUBSTRING(d, 10);
n := n/97;
WHILE d.LENGTH > 7 LOOP
n := n || SUBSTRING(d, 1, 7);
d := SUBSTRING(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
SUBSTRING is not a function in Oracle - you're looking for SUBSTR.
A variable such as d cannot be declared as VARCHAR2 - it must be given a length. Note that this is different from a parameter, such as p_IBAN, or a return value declaration - in both cases a length is not required (or even allowed).
#wweicker correctly points out that you cannot SELECT into a SUBSTR, and must instead use a variable.
When these errors are corrected I think your function should look something like:
CREATE OR REPLACE FUNCTION checkIBAN
(p_IBAN in varchar2)
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2(32767); -- max possible size for a VARCHAR2 var
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2(32767);
s VARCHAR2(32767);
BEGIN
v_landcode := SUBSTR(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if p_IBAN.LENGTH != v_lengte then
return 'F';
end if;
v_omgezettelandcode := SUBSTR(p_IBAN, 5) || SUBSTR(p_IBAN, 1, 4);
WHILE v_teller < v_omgezettelandcode.LENGTH LOOP
select getal
into s
from abc
where SUBSTR(v_omgezettelandcode, v_lengte, v_lengte) = letter;
v_omgezettelandcode := SUBSTR(vomgezettelandcode, 1, v_lengte-1) ||
letter ||
SUBSTR(vomgezettelandcode, v_lengte+LENGTH(letter));
v_teller := v_teller + 1;
END LOOP;
d := v_omgezettelandcode;
n := SUBSTR(d, 1, 9);
d := SUBSTR(d, 10);
n := n/97;
WHILE d.LENGTH > 7 LOOP
n := n || SUBSTR(d, 1, 7);
d := SUBSTR(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
Best of luck.
Share and enjoy.
You need to use CREATE OR REPLACE FUNCTION instead of just FUNCTION
ex.
CREATE OR REPLACE FUNCTION checkIBAN
( p_IBAN in varchar2 )
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2;
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2;
BEGIN
v_landcode := SUBSTRING(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if p_IBAN.LENGTH != v_lengte
then return 'F';
end if;
v_omgezettelandcode := SUBSTRING(p_IBAN, 5) || SUBSTRING(p_IBAN, 1, 4);
WHILE v_teller < v_omgezettelandcode.LENGTH LOOP
select getal
into SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte)
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
v_teller := v_teller + 1;
END LOOP;
d := v_omgezettelandcode;
n := SUBSTRING(d, 1, 9);
d := SUBSTRING(d, 10);
n := n/97;
WHILE d.LENGTH > 7 LOOP
n := n || SUBSTRING(d, 1, 7);
d := SUBSTRING(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
There is another error as well. Where you have:
select getal
into SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte)
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
You use INTO you must specify a variable. You can't specify the built in function 'SUBSTRING' to "select into"
ex.
select getal
into SOME_LOCAL_VARIABLE_NAME
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
thank you all, eventually i turned it over al little bit, now it works.
CREATE OR REPLACE FUNCTION checkIBAN
(p_IBAN in varchar2)
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2(32767); -- max possible size for a VARCHAR2 var
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2(32767);
s VARCHAR2(32767);
v_omgezet varchar2(32767);
v_number varchar2(32767);
BEGIN
v_landcode := SUBSTR(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if LENGTH(p_IBAN) != v_lengte then
return 'F';
end if;
v_omgezettelandcode := SUBSTR(p_IBAN, 5) || SUBSTR(p_IBAN, 1, 4);
while v_teller < LENGTH(v_omgezettelandcode) LOOP
if SUBSTR(v_omgezettelandcode, v_teller, v_teller) in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
then v_omgezet := v_omgezet || SUBSTR(v_omgezettelandcode, v_teller, v_teller);
else
select getal
into v_number
from abc
where letter = SUBSTR(v_omgezettelandcode, v_teller, v_teller);
v_omgezet := v_omgezet || v_number;
end if;
end loop;
d := v_omgezet;
n := SUBSTR(d, 1, 9);
d := SUBSTR(d, 10);
n := n/97;
WHILE LENGTH(d) > 7 LOOP
n := n || SUBSTR(d, 1, 7);
d := SUBSTR(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
Another potential solution for those using DbVisualizer, (and maybe other tools?). This is what solved this problem for me.
Add these two lines to your code, like so:
--/
(all your code)
/

Oracle plsql rtf varchar2 field to plain text format

I need to convert a rich formatted text of a VARCHAR2 field to plain text.
For example:
{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil Tahoma;}{\f1\fnil\fcharset0 Tahoma;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1\pard\cf1\lang1031\b\f0\fs16 NUMBER_A\cf0\b0\f1 *\cf1\b\protect NUMBER_B\cf0\b0\protect0\f0\par
}
should be converted to:
NUMBER_A * NUMBER_B
I have tried to parse the RTF-string char by char but this isn't a very smart solution.
A PL/SQL utilities method for any RTF-text would be the nicest way.
Is there a native solution? Any ideas how to convert the rtf-text?
Thx for sharing your time and ideas.
Apparently this can be done via Oracle Text - see this AskTom question
I made it with simple PL/SQL.
Based on this SQL source, but completly rewritten.
(Backslash + Apostrofe confuses code coloring here)
CREATE OR REPLACE FUNCTION Rtf2Txt
(
pRtf varchar2
)
return nvarchar2 is
/*
Converts RTF text to TXT format by removing headers, commands, and formatting
*/
vPos1 int;
vPos2 int;
vPos3 int;
vPos4 int;
vTmp int;
vText varchar2(4000);
begin
vText := pRtf;
if vText is null then
return vText;
end if;
-- Remove outer { and } pair
vPos1 := instr(vText, '{', +1); -- The first {
vPos2 := instr(vText, '}', -1); -- The last }
if vPos1 > 0 and vPos2 > 0 then
vText := substr(vText, vPos1 +1, vPos2 - vPos1 -1);
end if;
-- Remove inner { and } pairs
while 1 = 1 loop
vPos2 := instr(vText, '}', +1); -- The first }
vPos1 := instr(vText, '{', (length(vText) - vPos2) *-1 -1); -- The last { before the found }
if vPos1 > 0 and vPos2 > 0 and vPos1 < vPos2 then
vText := substr(vText, 1, vPos1 -1) || substr(vText, vPos2 +1, length(vText) - vPos2);
else
exit;
end if;
end loop;
-- Cleaning up
vText := replace(vText, '\pard', '');
vText := replace(vText, chr(13), '');
vText := replace(vText, chr(10), '');
vText := replace(vText, '\par', chr(13));
while length(vText) > 0 and substr(vText, 1, 1) IN (' ', CHR(13), CHR(10)) loop
vText := substr(vText, 2, length(vText) -1);
end loop;
while length(vText) > 0 and substr(vText, length(vText), 1) IN (' ', CHR(13), CHR(10)) loop
vText := substr(vText, 1, length(vText) -1);
end loop;
-- Remove \ commands and replace \'XX charactercoding
vPos2 := 1;
while 1 = 1 loop
vPos1 := instr(vText, '\', vPos2);
if vPos1 = 0 then
exit;
end if;
if substr(vText, vPos1 +1, 1) = '\' then -- Skip \\ escape sequence, when present
vPos2 := vPos1 +2;
continue;
end if;
if substr(vText, vPos1 +1, 1) = '''' then -- Decode \' hex sequence
vTmp := to_number(substr(vText, vPos1 +2, 2), 'xx');
vText := substr(vText, 1, vPos1 -1) ||chr(vTmp)|| substr(vText, vPos1 +4, length(vText) - vPos1 -3);
vPos2 := vPos1 +1;
continue;
end if;
-- Skip \anything sequence
vPos2 := instr(vText, '\', vPos1 +1); -- The next \
vPos3 := instr(vText, ' ', vPos1 +1); -- The next ' '
vPos4 := instr(vText, chr(13), vPos1 +1); -- The next Enter
if vPos4 > 0 and vPos4 < vPos3 then
vPos3 := vPos4;
end if;
if vPos2 = 0 and vPos3 = 0 then
vPos3 := length(vText);
end if;
if vPos2 > 0 and (vPos2 < vPos3 or vPos3 = 0) then
vText := substr(vText, 1, vPos1 -1) || substr(vText, vPos2, length(vText) - vPos2 +1);
vPos2 := vPos1;
end if;
if vPos3 > 0 and (vPos3 < vPos2 or vPos2 = 0) then
vText := substr(vText, 1, vPos1 -1) || substr(vText, vPos3 +1, length(vText) - vPos3);
vPos2 := vPos1;
end if;
end loop;
return vText;
end;
/
I dont'have the repuation to comment CLS's posting, but I would like to
advise that the line
if vPos1 = 0 then
should be changed to
if nvl(vPos1,0)=0 then
otherwise with not well formed RTFs the function will be stuck in the while loop forever
Bye
Andreas

Resources