Split the string into variables - oracle

After executing below select , How to assign it into variables.
If input string is "x/y/z" , I have to store "x" into variable say A ,"y" into variable B and z into variable "C".
Suppose if string is "x/z" then I have to store "x" into variable say A but z into variable "c".
in all other cases suppose if input string is only "x" or "x/y/z/z" then nothing can be stored
SELECT REGEXP_SUBSTR(<<Input String>>, '[^/]+', 1, LEVEL)
FROM DUAL
CONNECT BY REGEXP_SUBSTR((<<Input String>>, '[^/]+', 1, LEVEL)
IS NOT NULL;

Instead of using SQL, I would just use PL/SQL, since there seems no need to introduce an unnecessary context switch:
declare
v_a varchar2(10);
v_b varchar2(10);
v_c varchar2(10);
v_string varchar2(33);
procedure split_string (p_string in varchar2,
p_a out varchar2,
p_b out varchar2,
p_c out varchar2)
is
begin
if regexp_count(p_string, '/') = 2 then
p_a := regexp_substr(p_string, '[^/]+', 1, 1);
p_b := regexp_substr(p_string, '[^/]+', 1, 2);
p_c := regexp_substr(p_string, '[^/]+', 1, 3);
elsif regexp_count(p_string, '/') = 1 then
p_a := regexp_substr(p_string, '[^/]+', 1, 1);
p_c := regexp_substr(p_string, '[^/]+', 1, 2);
end if;
end;
begin
v_string := 'x/y/z';
split_string(v_string, v_a, v_b, v_c);
dbms_output.put_line('v_string = "'||v_string||'", v_a = "'||v_a||'", v_b = "'||v_b||'", v_c = "'||v_c||'"');
v_string := 'x/y';
split_string(v_string, v_a, v_b, v_c);
dbms_output.put_line('v_string = "'||v_string||'", v_a = "'||v_a||'", v_b = "'||v_b||'", v_c = "'||v_c||'"');
v_string := 'x/y/z/1';
split_string(v_string, v_a, v_b, v_c);
dbms_output.put_line('v_string = "'||v_string||'", v_a = "'||v_a||'", v_b = "'||v_b||'", v_c = "'||v_c||'"');
v_string := 'x';
split_string(v_string, v_a, v_b, v_c);
dbms_output.put_line('v_string = "'||v_string||'", v_a = "'||v_a||'", v_b = "'||v_b||'", v_c = "'||v_c||'"');
end;
/
v_string = "x/y/z", v_a = "x", v_b = "y", v_c = "z"
v_string = "x/y", v_a = "x", v_b = "", v_c = "y"
v_string = "x/y/z/1", v_a = "", v_b = "", v_c = ""
v_string = "x", v_a = "", v_b = "", v_c = ""
If you absolutely must use SQL, there is no need to use the connect by - you can just separate the results into 3 columns to match the 3 variables you want to input the results into:
with strings as (select 'x/y/z' str from dual union all
select 'x/y' str from dual union all
select 'x/y/z/1' str from dual union all
select 'x' str from dual)
select str,
case when regexp_count(str, '/') in (1, 2) then regexp_substr(str, '[^/]+', 1, 1) end v_a,
case when regexp_count(str, '/') = 2 then regexp_substr(str, '[^/]+', 1, 2) end v_b,
case when regexp_count(str, '/') = 2 then regexp_substr(str, '[^/]+', 1, 3)
when regexp_count(str, '/') = 1 then regexp_substr(str, '[^/]+', 1, 2)
end v_c
from strings;
STR V_A V_B V_C
------- --------------------- --------------------- ---------------------
x/y/z x y z
x/y x y
x/y/z/1
x

Be careful, regex_substr using the format '[^/]+' to parse the string elements do not handle null list elements. Here's a way to extract a specific element from a list that handles nulls, which can be put into a function for reuse (this gets the first element where the separator is a slash):
REGEXP_SUBSTR(string_in, '(.*?)(/|$)', 1, 1, NULL, 1);
See the link above, but call like this simple example where the list elements are extracted in order by a function called get_list_element and assigned to variables. Perhaps you can apply this logic to your needs:
SQL> declare
2 a varchar2(1);
3 b varchar2(1);
4 c varchar2(1);
5 begin
6 select get_list_element('x/y/z', 1, '/'),
7 get_list_element('x/y/z', 2, '/'),
8 get_list_element('x/y/z', 3, '/')
9 into a, b, c
10 from dual;
11
12 dbms_output.put_line('a: ' || a);
13 dbms_output.put_line('b: ' || b);
14 dbms_output.put_line('c: ' || c);
15 end;
16 /
a: x
b: y
c: z
PL/SQL procedure successfully completed.
SQL>
Or fix your original try at turning your elements into rows with this:
SQL> with tbl(str) as (
2 select 'x/y/z' from dual
3 )
4 select regexp_substr(str, '(.*?)(/|$)', 1, level, null, 1) element
5 from tbl
6 connect by level <= regexp_count(str, '/')+1;
ELEME
-----
x
y
z
SQL>

Related

TOP with index variable oracle

I was trying to use the variable 'ind' to get the ROWNUM on my select, but everytime I try to use the variable there I get a error like:
ORA-01008: not all variables bound
or these two:
ORA-01403: no data found
ORA-06512: at line 10
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
LOOP
ind := ind + 1;
IF ind > 3 THEN
EXIT;
END IF;
SELECT TEXTO_LOG
INTO texto
from table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração') AND ROWNUM >= :ind AND ROWNUM <= :ind ;
dbms_output.put_line(substr(trim(texto), 1, instr(texto, ' ')));
dbms_output.put_line(substr(texto, 0, 100));
END LOOP;
END;
/
I searched and found someone telling that not all variables bound is a bug, I'm not sure if it's.
I tried the ROWNUM with different opperators. Any suggestions?
Usage of rownum is the problem here apart from other issues already answered. a predicate like rownum>=2 and rownum <=2 (and so on...till exit point) yield no result and thus no_data_found error
But as workaround put the rownum inside from clause and restrict it outside should work,
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
LOOP
ind := ind + 1;
IF ind > 3
THEN
EXIT;
END IF;
SELECT texto_log
INTO texto
FROM (SELECT texto_log
,rownum myrownum
FROM TABLE
WHERE regexp_like(texto_log
,'Alteração'))
WHERE myrownum >= ind
AND myrownum <= ind;
dbms_output.put_line(substr(TRIM(texto)
,1
,instr(texto
,' ')));
dbms_output.put_line(substr(texto
,0
,100));
END LOOP;
END;
/
The error is that you are referring to :ind as if it were a bind variable, when it is not. It is in fact a variable declared in your DECLARE section.
You might try this
** Update **
As your query looks like it is faling, the reason perhaps is in the query itself. Run this and get the output and running separately
set serveroutput on size unlimited
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
for r in 1..4
LOOP
ind := r + 1;
dbms_output.put_line ( q'[SELECT TEXTO_LOG
INTO texto
from table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração') AND ROWNUM <= ind ;
dbms_output.put_line(substr(trim(texto), 1, instr(texto, ' ')));
dbms_output.put_line(substr(texto, 0, 100));]');
exit when ind > 3;
END LOOP;
END;
/
Although you can build it like this which is easier
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
for r in 1..4
LOOP
ind := r + 1;
dbms_output.put_line(ind);
SELECT TEXTO_LOG
INTO texto
from table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração') AND ROWNUM >= ind AND ROWNUM <= ind ;
dbms_output.put_line(substr(trim(texto), 1, instr(texto, ' ')));
dbms_output.put_line(substr(texto, 0, 100));
exit when ind > 3;
END LOOP;
END;
/
EXample
SQL> DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
for r in 1..4
LOOP
ind := r + 1;
dbms_output.put_line(ind);
exit when ind > 3;
END LOOP;
END;
/ 2 3 4 5 6 7 8 9 10 11 12
2
3
4
PL/SQL procedure successfully completed.
SQL>
With implicit cursor
DECLARE
-- texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
FOR i IN (SELECT TEXTO_LOG FROM table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração'))
LOOP
ind:=ind+1;
EXIT WHEN ind >3;
DBMS_OUTPUT.PUT_LINE(SUBSTR(TRIM(i.TEXTO_LOG), 1, INSTR(i.TEXTO_LOG, ' ')));
DBMS_OUTPUT.PUT_LINE(SUBSTR(i.TEXTO_LOG, 0, 100));
END LOOP;
END;

PL/SQL function returns minimal number

How to write a simple function that returns minimal salary. IN parameters are salary1,salary2,salary3 (9240, 9750, 8320) and it is forbidden to use finished functions.
I have no code whatsoever. I am very new at this and am trying to learn something.
The function is already exists in PLSQL. Check it out here. There are good examples of how to use it
UPD: As per Pavel mentioned here is a pair of examples
SQL:
select least(100, 1, 200) from dual; -- returns 1
PLSQL:
create or replace procedure get_min(s1 number, s2 number, s3 number)
is
min_sal number := 0;
begin
min_sal := least(s1, s2, s3);
dbms_output.put_line('min: ' || min_sal);
end;
if it is not allowed to use existing functions, you can do it using If then else
create or replace function get_min(s1 number, s2 number, s3 number)
return number
is
min_sal number := 0;
begin
---------------------
-- if the first value is less than the second value
-- , then the first value is the min of both values
-- otherwise the second value is min
if s1 < s2 then
min_sal := s1;
else
min_sal := s2;
end if;
------------------------------------
-- now you check the new min value against the third value.
if s3 < min_sal then
min_sal := s3;
end if;
dbms_output.put_line('min: ' || min_sal);
return min_sal;
end;
/
DECLARE
v_result NUMBER;
BEGIN
v_result := GET_MIN(1, 1, 1);
v_result := GET_MIN(2, 1, 1);
v_result := GET_MIN(1, 2, 1);
v_result := GET_MIN(1, 1, 2);
v_result := GET_MIN(2, 2, 1);
v_result := GET_MIN(1, 2, 2);
v_result := GET_MIN(1, 2, 3);
v_result := GET_MIN(2, 1, 3);
v_result := GET_MIN(3, 2, 1);
END;
/
Result:
dbms_output:
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
db<>fiddle here

increment function in plsql varchar2

I want use this in a varchar2. Ex.:
declare
num number := &Number;
serie varchar2(200) := 'S = ';
begin
for x in 1 .. num loop
serie += x, ' + ';
end loop; `
end;
/
In the end I want that the serie be like "S = 1 + 2 + 3 ..." How can i make that work?
That would be something like this:
SQL> set serveroutput on
SQL> declare
2 num number := &Number;
3 serie varchar2(200) := 'S = ';
4 begin
5 for x in 1 .. num loop
6 serie := serie || to_char(x) || ' + ';
7 end loop;
8
9 -- remove the trailing "+"
10 serie := rtrim(serie, ' +');
11 dbms_output.put_Line(serie);
12 end;
13 /
Enter value for number: 5
S = 1 + 2 + 3 + 4 + 5
PL/SQL procedure successfully completed.
SQL>
A few comments:
line 6: you have to concatenate (concatenation operator is a double pipe sign, ||) previous value of SERIE; otherwise, you'd have only the last number in it
line 10: remove the trailing "+" sign
What could be done in pure SQL, usually should be done in pure SQL:
declare
num number := &Number;
serie varchar2(200);
begin
select 'S = ' || listagg(rownum, ', ') within group (order by rownum)
into serie
from dual
connect by level <= num;
dbms_output.put_line(serie);
end;
/
The result for num = 10:
S = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Note, listagg function was introduced in Oracle version 11.2.

Lowest fraction Value In Oracle

I am trying to create a function to return lowest fraction value. the sample code is here :
create or replace function fraction_sh(x number) return varchar2
is
fra1 number;
pwr number;
intprt number;
v4 number;
numer number;
denom number;
gcdval number;
frac varchar2(50);
begin
if x <> 0 then
fra1 := mod(x,1);
pwr := length(mod(x,1))-1;
intprt := trunc(x);
numer :=mod(x,1)*power(10,length(mod(x,1))-1);
denom :=power(10,length(mod(x,1))-1);
gcdval := gcdnew(power(10,length(mod(x,1))-1),mod(x,1)*power(10,length(mod(x,1))-1));
if intprt = 0 then
frac := to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
DBMS_OUTPUT.put_line(1||' '||denom||' '||gcdval||' '||numer);
else
frac := (intprt*to_char(trunc(denom/gcdval)))+to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
DBMS_OUTPUT.put_line(2||' '||denom||' '||gcdval||' '||numer);
end if;
end if;
return frac;
end;
create or replace function gcdnew (a number, b number, p_precision number default null, orig_larger_num number default null) return number is
v_orig_larger_num number := greatest(nvl(orig_larger_num,-1),a,b);
v_precision_level number := p_precision;
begin
if a is null or b is null or (a = 0 and b = 0) then return 1; end if;
if p_precision is null or p_precision <= 0 then
v_precision_level := 4;
end if;
if b is null or b = 0 or (b/v_orig_larger_num <= power(10,-1*v_precision_level) and greatest(a,b) <> v_orig_larger_num) then
return a;
else
return (gcdnew(b,mod(a,b),v_precision_level,v_orig_larger_num));
end if;
end;
Inmost cases it works, but when i try to pass 2/11 it returns 2/10.
Any help appreciated.
The problem with what you're currently doing is precision. With 2/11 the resulting number is 0.1818181... recurring, and the length of that - and therefore the pwr value - end up as 40, which destroys the later calculations.
With modifications to limit the precision (and tidied up a bit, largely to remove repeated calculations when you have handy variables already):
create or replace function fraction_sh(p_float number) return varchar2
is
l_precision pls_integer := 10;
l_int_part pls_integer;
l_frac_part number;
l_power pls_integer;
l_numer number;
l_denom number;
l_gcdval number;
l_result varchar2(99);
begin
if p_float is null or p_float = 0 then
return null;
end if;
l_int_part := trunc(p_float);
l_frac_part := round(mod(p_float, 1), l_precision);
l_power := length(l_frac_part);
l_denom := power(10, l_power);
l_numer := l_frac_part * l_denom;
l_gcdval := gcdnew(l_denom, l_numer, ceil(l_precision/2));
if l_int_part = 0 then
l_result := trunc(l_numer/l_gcdval) ||'/'|| trunc(l_denom/l_gcdval);
else
l_result := l_int_part * (trunc(l_denom/l_gcdval) + trunc(l_numer/l_gcdval))
||'/'|| trunc(l_denom/l_gcdval);
end if;
return l_result;
end;
/
Which gets:
with t(n) as (
select 9/12 from dual
union all select 2/11 from dual
union all select 1/2 from dual
union all select 1/3 from dual
union all select 1/4 from dual
union all select 1/5 from dual
union all select 1/6 from dual
union all select 1/7 from dual
union all select 1/8 from dual
union all select 1/9 from dual
union all select 1/10 from dual
union all select 4/3 from dual
union all select 0 from dual
union all select 1 from dual
)
select n, fraction_sh(n) as fraction
from t;
N FRACTION
---------- ------------------------------
.75 3/4
.181818182 2/11
.5 1/2
.333333333 1/3
.25 1/4
.2 1/5
.166666667 1/6
.142857143 1/7
.125 1/8
.111111111 1/9
.1 1/10
1.33333333 4/3
0
1 1/1
So you might want to add some handling for either passing in 1, or the approximation after rounding ending up as 1/1 - presumably just to return a plain '1' in either case.
I've set l_precision to 10 rather arbitrarily, you can make that larger, but will hit problems at some point so test carefully with whatever value you pick.
(And I haven't looked at gdcnew at all; that can probably be simplified a bit too.)
you can use like this:
create or replace function fraction_sh(dividing number,divided number) return varchar2
is
dividing2 number;
divided2 number;
frac varchar2(100 char);
temp number;
loop_value boolean;
begin
loop_value:=true;
dividing2:=dividing;
divided2 :=divided;
if dividing <> 0 then
while loop_value
loop
if gcd(dividing2,divided2)<> 1 then
temp:=gcd(dividing2,divided2);
dividing2:=dividing2/temp;
divided2 :=divided2/temp;
frac:=dividing2||'/'||divided2;
else
loop_value:=false;
frac:=dividing2||'/'||divided2;
end if;
end loop;
else
frac:='0';
end if;
return frac;
end;
gcd func:
create or replace function gcd(a number, b number)
return number is
begin
if b = 0 then
return a;
else
return gcd(b,mod(a,b));
end if;
end;

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)
/

Resources