How to find the length of an input string without using standard length function in PL/SQL? - sql

Is there a way to know the string length by writing PL/SQL function, without using LENGTH()?

CREATE OR REPLACE FUNCTION mylength (p_text IN VARCHAR2)
RETURN INTEGER
AS
l_ret INTEGER := 0;
BEGIN
WHILE SUBSTR (p_text, l_ret + 1, 1) IS NOT NULL
LOOP
l_ret := l_ret + 1;
END LOOP;
RETURN l_ret;
END mylength;
BEGIN
DBMS_OUTPUT.put_line ('ABC: ' || mylength ('ABC'));
DBMS_OUTPUT.put_line ('ABCDEFG: ' || mylength ('ABCDEFG'));
DBMS_OUTPUT.put_line ('empty: ' || mylength (''));
DBMS_OUTPUT.put_line ('null: ' || mylength (NULL));
END;
ABC: 3
ABCDEFG: 7
empty: 0
null: 0

Related

Adding output to function PL/SQL

I am trying to get my function to include the date and current user, but I keep getting an error that my function is in an invalid state. If I comment out or remove the two dbms_output... lines it works just fine. Any ideas of how to return that output? Using Oracle SQL Developer
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
str2 VARCHAR2(10) := y;
BEGIN
RETURN str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;
You have to declare the result Variable first, also enable the dbms_output on your SQL Developer.
This should work.
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
STR2 VARCHAR2(10) := Y;
result VARCHAR2(250);
BEGIN
result := str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result;
END;
/
SELECT F_CONCATENATE_STRINGS('Crypto','Currency') FROM DUAL;
your function has some issues.
result is not declared
return statement should be the last statement in the function. it return a result and terminates the execution of the function
you should convert to char the sysdate
you can try this one:
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
--str1 VARCHAR2(10) := x;
--str2 VARCHAR2(10) := y;
result VARCHAR2(20);
BEGIN
result := x || y;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || to_char(SYSDATE,'dd.mm.yyyy') || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result; -- move to the end of the function
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;

Building an array based on comma seperated string -oracle

I want to build an array based on a comma separated string . But cant hwlp myslef any further .
DECLARE
type rol_type is record
(role MMSTROLEHDR.ROLECODE%TYPE) ;
array_rolecode rol_type;
vl_prmval VARCHAR2 (4000) := '2,3,4';
vl_pos NUMBER;
BEGIN
WHILE (INSTR (vl_prmval, ',') > 0)
LOOP
vl_pos := INSTR (vl_prmval, ',');
--vl_cnt := vl_cnt + 1;
array_rolecode.role := SUBSTR (vl_prmval, 1, vl_pos - 1);
vl_prmval := SUBSTR (vl_prmval, vl_pos + 1);
END LOOP;
FOR j IN array_rolecode.first .. array_rolecode.last
LOOP
DBMS_OUTPUT.put_line (array_rolecode.role);
END LOOP;
END;
END;
Your array must be declared as array, not as a record. It seems, there is an error in cycle, cause the last part of the string not being inserted in array, you should check it. However, here is the working script:
DECLARE
type rol_type is record
(role MMSTROLEHDR.ROLECODE%TYPE) ;
type rol_arr is table of rol_type
index by binary_integer;
--array_rolecode rol_type;
array_rolecode rol_arr;
vl_prmval VARCHAR2 (4000) := '2,3,4';
vl_pos NUMBER := 0;
BEGIN
WHILE (INSTR (vl_prmval, ',') > 0)
LOOP
vl_pos := INSTR (vl_prmval, ',');
--vl_cnt := vl_cnt + 1;
array_rolecode(NVL(array_rolecode.last+1,1)).role := SUBSTR (vl_prmval, 1, vl_pos - 1);
vl_prmval := SUBSTR (vl_prmval, vl_pos + 1);
END LOOP;
FOR j IN array_rolecode.first .. array_rolecode.last
LOOP
DBMS_OUTPUT.put_line (array_rolecode(j).role);
END LOOP;
END;

Selection of columns in stored procedure based on parameters

I need to check the parameters in the stored procedure if it is entered then I need to select that
i=j=k=l=m=1;
IF (p_plant_cd IS NULL) THEN
i=0;
END IF;
IF(p_global_duns_nbr IS NULL) THEN
j=0
END IF;
IF(p_global_duns_nbr IS NULL) THEN
k=0
END IF;
IF(p_matrl_grp IS NULL) THEN
l=0
END IF;
IF (p_mrp IS NULL) THEN
m=0
END IF ;
Which ever value is 1 I need to add corresponding parameters in the variable v_select
For eg ;
if k and l are 1 then
v_select='p_global_duns_nbr,p_matrl_grp'
Pls suggest me how to do this.
You can declare a variable , holding column names , then concat to select statement and you have SQL select statement in v_select , use it as you want
declare
v_columns varchar2(255);
v_select varchar2(2000);
v_result sys_refcursor;
begin
v_columns := 'rownum';
IF p_plant_cd = 1 THEN
v_columns := v_columns || ',p_plant_cd';
END IF;
IF p_global_duns_nbr = 1 THEN
v_columns := v_columns || ',p_global_duns_nbr';
END IF;
IF p_global_duns_nbr = 1 THEN
v_columns := v_columns || ',p_global_duns_nbr';
END IF;
IF p_matrl_grp = 1 THEN
v_columns := v_columns || ',p_matrl_grp';
END IF;
IF p_mrp = 1 THEN
v_columns := v_columns || ',p_mrp';
END IF;
v_select := 'SELECT ' || v_columns || ' FROM table';
open v_result for v_select;
end;

pl/sql - can collection loop through column names?

The output from the below code is:
|LAT|MISC|SID|NO
MIN_LENGTH|1|2|1|1
MAX_LENGTH|6|6|4|2
The output is as I expect, but is there anyway to loop through the columns using an index (ie. j) instead of doing RESULTS(I).MAX_LENGTH , RESULTS(I).MAX_LENGTH etc ? The concern is that when adding extra columns to the 'R_RESULT_REC' record, another loop is required.
set serveroutput on
DECLARE
TYPE R_RESULT_REC IS RECORD
(COL_NAME VARCHAR2(100),
MIN_LENGTH NUMBER,
MAX_LENGTH NUMBER
);
TYPE tr_RESULT IS TABLE OF R_RESULT_REC;
RESULTS TR_RESULT := TR_RESULT();
v_counter NUMBER := 1;
BEGIN
FOR J IN (SELECT DISTINCT COLUMN_NAME FROM ALL_TAB_COLUMNS
WHERE OWNER = 'SYSTEM'
and TABLE_NAME = 'SPECCHAR')
LOOP
RESULTS.EXTEND;
RESULTS(V_COUNTER).COL_NAME := J.COLUMN_NAME;
EXECUTE IMMEDIATE 'SELECT MIN(LENGTH('||J.COLUMN_NAME||')),
MAX(LENGTH('||J.COLUMN_NAME||'))
FROM '||'SYSTEM'||'.'||'SPECCHAR' INTO
RESULTS(V_COUNTER).MIN_LENGTH,
RESULTS(V_COUNTER).MAX_LENGTH;
V_COUNTER := V_COUNTER + 1;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).COL_NAME);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT(' |'||RESULTS(I).COL_NAME||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).COL_NAME||'|');
END IF ;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).MIN_LENGTH);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT('MIN_LENGTH|'||RESULTS(I).MIN_LENGTH||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).MIN_LENGTH||'|');
END IF ;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).MAX_LENGTH);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT('MAX_LENGTH|'||RESULTS(I).MAX_LENGTH||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).MAX_LENGTH||'|');
END IF ;
END LOOP;
end;
This uses DBMS_SQL, so it's pretty snarly to read. The main reason I saw to use it was that I could get columnar descriptions of a SQL statement and to a buffer-based, not object-based fetch.
Rather than making calls to DBMS_OUTPUT during the processing, it builds a table of records for output, using associative arrays for simplicity.
It could further be refined to have an array or parsable list of functions to apply to each function, but that seems excess to current requirements. The nature of the code would require editing if new aggregation functions are being added.
Call overview (2c + a + s):
3 loops;
2 loops over column list (c),
1 loop over number of analytic functions (a).
1 SQL statement against table data (s).
OP's call overview (c*s + a + 1):
1 loop, executing a sql statement against table data per column (c*s)
a+1 loops, where a is the number of analytic functions
Test data:
1 select min(length(GP_ID)), max(length(GP_ID)),
2 min(length(GGP_ID)), max(length(GGP_ID)),
3 min(length(OBJECT_NAME)), max(length(OBJECT_NAME))
4* from AMUSCH.GP
SQL> /
MIN(LENGTH(GP_ID)) MAX(LENGTH(GP_ID)) MIN(LENGTH(GGP_ID))
MAX(LENGTH(GGP_ID)) MIN(LENGTH(OBJECT_NAME)) MAX(LENGTH(OBJECT_NAME))
1 7 1
4 9 41
Code:
declare
p_owner varchar2(30);
p_table_name varchar2(30);
TYPE OUTPUT_TAB_TYPE IS TABLE OF VARCHAR2(32767) index by binary_integer;
OUTPUT_TAB OUTPUT_TAB_TYPE;
l_columns_tab dbms_sql.desc_tab;
l_columns_cur integer;
l_columns_sql varchar2(32767);
l_columns_cnt number;
l_minmax_sql varchar2(32767);
l_minmax_cur integer;
l_minmax_tab dbms_sql.desc_tab;
l_minmax_cnt number;
l_fetch_ok number;
l_fetch_value number;
begin
p_owner := 'AMUSCH';
p_table_name := 'GP';
output_tab(1) := lpad(' ', 20, ' ');
output_tab(2) := lpad('MIN_LENGTH', 20, ' ');
output_tab(3) := lpad('MAX_LENGTH', 20, ' ');
l_columns_sql := 'select * from ' || p_owner || '.' || p_table_name ||
' where 1 = 0';
l_columns_cur := dbms_sql.open_cursor;
dbms_sql.parse (l_columns_cur, l_columns_sql, dbms_sql.native);
dbms_sql.describe_columns (l_columns_cur, l_columns_cnt, l_columns_tab);
-- build the min/max sql statement
l_minmax_sql := 'select ' ;
for i in 1..l_columns_cnt
loop
l_minmax_sql := l_minmax_sql ||
' min(length(' || l_columns_tab(i).col_name || ')), ';
l_minmax_sql := l_minmax_sql ||
' max(length(' || l_columns_tab(i).col_name || ')), ';
end loop;
l_minmax_sql := substr(l_minmax_sql, 1,
length(l_minmax_sql) - 2); -- trim trailing comma
l_minmax_sql := l_minmax_sql || ' from ' || p_owner || '.' || p_table_name;
l_minmax_cur := dbms_sql.open_cursor;
dbms_sql.parse (l_minmax_cur, l_minmax_sql, dbms_sql.native);
dbms_sql.describe_columns (l_minmax_cur, l_minmax_cnt, l_minmax_tab);
for i in 1..l_minmax_cnt
loop
dbms_sql.define_column(l_minmax_cur, i, l_fetch_value);
end loop;
l_fetch_ok := dbms_sql.execute(l_minmax_cur);
loop
l_fetch_ok := dbms_sql.fetch_rows(l_minmax_cur);
exit when l_fetch_ok = 0;
-- loop over the columns selected over
for i in 1..l_columns_cnt
loop
output_tab(1) := output_tab(1) || '|' || l_columns_tab(i).col_name;
dbms_sql.column_value(l_minmax_cur, (2*i-1), l_fetch_value);
output_tab(2) := output_tab(2) || '|' ||
lpad(l_fetch_value, length(l_columns_tab(i).col_name), ' ');
dbms_sql.column_value(l_minmax_cur, (2*i), l_fetch_value);
output_tab(3) := output_tab(3) || '|' ||
lpad(l_fetch_value, length(l_columns_tab(i).col_name), ' ');
end loop;
end loop;
if dbms_sql.is_open(l_minmax_cur) then
dbms_sql.close_cursor (l_minmax_cur);
end if;
if dbms_sql.is_open (l_columns_cur) then
dbms_sql.close_cursor (l_columns_cur);
end if;
for i in output_tab.first..output_tab.last
loop
dbms_output.put_line(output_tab(i));
end loop;
end;
/
Results:
|GP_ID|GGP_ID|OBJECT_NAME
MIN_LENGTH| 1| 1| 9
MAX_LENGTH| 7| 4| 41
If you want to use the DBMS_SQL package (which is sometimes very complex), then there is a DBMS_SQL.COLUMN_VALUE function that may work for you.
update:
Or even better: DBMS_SQL.DESC_REC
you can refer to:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#i996963
notice example 8
I haven't tested it
update:
Perhaps what you really want is to loop on an Object type attributes and not a table column, so maybe you should try a different approach:
Make your type R_RESULT_REC an Object type in the DB and then you can loop on the query results:
SELECT attr_name
FROM user_type_attrs
WHERE type_name = 'R_RESULT_REC'
It's not like working with indexes but you still don't need to hard code the column names / type attributes
here is the code (based on yours):
CREATE OR REPLACE TYPE R_RESULT_REC AS OBJECT
(
COL_NAME VARCHAR2(100),
MIN_LENGTH NUMBER,
MAX_LENGTH NUMBER
);
/
and then:
DECLARE
TYPE tr_RESULT IS TABLE OF R_RESULT_REC;
RESULTS TR_RESULT := TR_RESULT();
v_counter NUMBER := 1;
v_max number;
v_min number;
BEGIN
FOR J IN (SELECT DISTINCT COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE OWNER = 'SYSTEM'
and TABLE_NAME = 'SPECCHAR') LOOP
EXECUTE IMMEDIATE 'SELECT MIN(LENGTH(' || J.COLUMN_NAME || ')),
MAX(LENGTH(' || J.COLUMN_NAME || ')) FROM ' ||
'SPECCHAR'
INTO v_min, v_max;
RESULTS.EXTEND;
RESULTS(V_COUNTER) := new R_RESULT_REC(J.COLUMN_NAME, v_min, v_max);
V_COUNTER := V_COUNTER + 1;
END LOOP;
for r in (select attr_name
from all_type_attrs t
where t.owner = 'SYSTEM'
and t.type_name = 'R_RESULT_REC') loop
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT_LINE(rec.' || r.attr_name || ');' ||
'end;'
using RESULTS(I);
ELSIF I = RESULTS.FIRST THEN
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT(''' || r.attr_name ||
'|'' || rec.' || r.attr_name || ' || ''|'');' ||
'end;'
using RESULTS(I);
ELSE
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT(rec.' || r.attr_name ||
' || ''|''); ' || 'end;'
using RESULTS(I);
END IF;
END LOOP;
end loop;
end;
If you'll add another attribute to the Record (and initiate it with values) , it will automatic display it.
Take advantage of Oracle's stats for this.
First, fully build stats on table using dbms_stats.gather_table_stats
Then, create the following function to help translate the raw low/high values that Oracle stores in all_tab_columns
create or replace function show_raw(i_raw raw, i_type varchar2)
return varchar2 is
l_varchar2 varchar2(32);
l_number number;
l_date date;
l_nvarchar2 nvarchar2(32);
l_rowid rowid;
l_char char;
begin
if (i_type = 'VARCHAR2') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_varchar2);
return to_char(l_varchar2);
elsif(i_type = 'NUMBER') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_number);
return to_char(l_number);
elsif(i_type = 'DATE') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_date);
return to_char(l_date);
elsif(i_type = 'NVARCHAR2') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_nvarchar2);
return to_char(l_nvarchar2);
elsif(i_type = 'ROWID') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_rowid);
return to_char(l_rowid);
elsif(i_type = 'CHAR') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_char);
return l_char;
else return 'Unknown type value';
end if;
end;
Then, just select the low/high values for each column:
select column_id,
column_name,
data_type,
show_raw(low_value, data_type) as min_val,
show_raw(high_value, data_type) as max_val
from all_tab_columns
where table_name = 'SOME_TABLE'
and owner = 'SOME_OWNER'
;

is it possible to have alphanumeric sequence generator in sql

I need to write a SQL query to print the following aphanumberic sequence in SQL
0001,
0002,
... ,
0009,
000A,
... ,
000Z,
... ,
0010,
0011,
... ,
001A,
... and so on till... ,
ZZZZ
please note: all characters are UPPERCASE.
Thanks in advance
You could create a function like this:
create function to_base_36 (n integer) return varchar2
is
q integer;
r varchar2(100);
begin
q := n;
while q >= 36 loop
r := chr(mod(q,36)+case when mod(q,36) < 10 then 48 else 55 end) || r;
q := floor(q/36);
end loop;
r := chr(mod(q,36)+case when mod(q,36) < 10 then 48 else 55 end) || r;
return lpad(r,4,'0');
end;
and then use it like this:
select rownum, to_base_36(rownum)
from dual
connect by level < 36*36*36*36;
Or, without creating a function:
with digits as
( select n, chr(mod(n,36)+case when mod(n,36) < 10 then 48 else 55 end) d
from (Select rownum-1 as n from dual connect by level < 37)
)
select d1.n*36*36*36 + d2.n*36*36 + d3.n*36 + d4.n, d1.d||d2.d||d3.d||d4.d
from digits d1, digits d2, digits d3, digits d4
You could use this function:
create or replace FUNCTION SEQGEN(vinp in varchar2, iSeq in INTEGER)
RETURN VARCHAR2 is vResult VARCHAR2(32);
iBas INTEGER; iRem INTEGER; iQuo INTEGER; lLen CONSTANT INTEGER := 2;
BEGIN
iBas := length(vInp);
iQuo := iSeq;
WHILE iQuo > 0 LOOP
iRem := iQuo mod iBas;
--dbms_output.put_line('Now we divide ' || lpad(iQuo,lLen,'0') || ' by ' || lpad(iBas,lLen,'0') || ', yielding a quotient of ' || lpad( TRUNC(iQuo / iBas) ,lLen,'0') || ' and a remainder of ' || lpad(iRem,lLen,'0') || ' giving the char: ' || substr(vInp, iRem, 1));
iQuo := TRUNC(iQuo / iBas);
If iRem < 1 Then iRem := iBas; iQuo := iQuo - 1; End If;
vResult := substr(vInp, iRem, 1) || vResult;
END LOOP;
RETURN vResult;
END SEQGEN;
Try the function:
SELECT * FROM (
SELECT seqgen('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',rownum + 47989 --start value
) Output, level evt FROM dual CONNECT BY level < 1679618) --stop value
WHERE mod(evt,50000) = 0 OR output in ('0001','0002','0009','000A','000Z',
'0010','0011','001A','ZZZZ')
Note that if you change the string you must also change the start and stop value.
Read more about number systems here: Number System Conversion - Explanation
-- To get 00000 to ZZZZZ next auto alphanumeric sequence using this function [Please verify before use]
-- This starts from 0-9 then A-Z and then increase next digit from 0-9 then A-Z
-- You need to pass the starting/Last sequence as value to get next sequence
CREATE OR REPLACE FUNCTION return_next_seq (curr_sequence VARCHAR2)
RETURN VARCHAR2 IS
retval VARCHAR2(4000) := NULL;
retMaxval VARCHAR2(4000) := NULL;
eval_digit CHAR(1) := NULL;
original_sequence VARCHAR2(4000) := curr_sequence;
curr1_sequence VARCHAR2(4000) := curr_sequence;
BEGIN
retval := original_sequence;
FOR j IN REVERSE 1..LENGTH(curr1_sequence) LOOP -- Using reverse to know
-- the exact digit position
eval_digit := SUBSTR(curr1_sequence, LENGTH(curr1_sequence));
--IF (ASCII(eval_digit) BETWEEN 49 AND 56) OR
--(ASCII(eval_digit) BETWEEN 97 AND 121) THEN
IF (ASCII(eval_digit) BETWEEN 48 AND 56) OR
(ASCII(eval_digit) BETWEEN 65 AND 89) THEN
eval_digit := CHR(ASCII(eval_digit) +1);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
retval := curr1_sequence || eval_digit || SUBSTR(original_sequence,
LENGTH(curr1_sequence || eval_digit)+1);
EXIT;
ELSE -- move to the next digit leaving the evaluated digit untouched.
IF (ASCII(eval_digit) = 57) THEN
eval_digit := CHR(ASCII(eval_digit) +8);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
retval := curr1_sequence || eval_digit || SUBSTR(original_sequence,
LENGTH(curr1_sequence || eval_digit)+1);
EXIT;
END IF;
IF (ASCII(eval_digit) = 90) THEN
retMaxval := eval_digit;
eval_digit := CHR(ASCII(eval_digit) -42);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
FOR k IN REVERSE 1..LENGTH(curr1_sequence) LOOP
IF (ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) BETWEEN 48 AND 56) OR (ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) BETWEEN 65 AND 89) THEN
retval := SUBSTR(curr1_sequence,0,LENGTH(curr1_sequence)-1) || CHR(ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence)))+1) || eval_digit || SUBSTR(retval,
LENGTH(curr1_sequence || eval_digit)+1);
ELSE
IF ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) = 57 THEN
retval := SUBSTR(curr1_sequence,0,LENGTH(curr1_sequence)-1) || CHR(65) || eval_digit || SUBSTR(retval,
LENGTH(curr1_sequence || eval_digit)+1);
ELSE
IF ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) = 90 AND LENGTH(curr1_sequence)>1 THEN
retval := SUBSTR(curr1_sequence,0,LENGTH(curr1_sequence)-1) || CHR(48) || eval_digit || SUBSTR(retval,
LENGTH(curr1_sequence || eval_digit)+1);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
retMaxval := retMaxval||'Z';
ELSE
retMaxval := retMaxval||'Z';
EXIT;
END IF;
END IF;
END IF;
END LOOP;
EXIT;
ELSE
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
END IF;
END IF;
END LOOP;
IF retval IS NULL OR (LENGTH(retval) = LENGTH(retMaxval)) THEN
RETURN 'MAX';
END IF;
RETURN retval;
END;
-- To verify, call this function like
SELECT return_next_seq('ZY9Z') FROM dual;
-- Any improvement suggestion is welcome!
--- Thanks!..Sanjiv