is it possible to have alphanumeric sequence generator in sql - 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

Related

ORA-06502: PL/SQL: numeric or value error: character string buffer too small only three numbers

create or replace FUNCTION "FNC_CALCULATE_MOD11" (P_VALOR IN NUMBER)
return number is
Result number;
begin
DECLARE
-- LOCAL VARIABLES HERE
V_PROCESSO VARCHAR2(30);
V_PESO NUMBER := 2;
V_SOMA NUMBER := 0;
V_RESTO NUMBER := 0;
BEGIN
V_PROCESSO := TO_CHAR(P_VALOR);
WHILE LENGTH(V_PROCESSO) < 6 --Popular com zeros no inicio até 6
LOOP
V_PROCESSO := '0'||V_PROCESSO;
END LOOP;
--accuses error on this line
FOR I IN REVERSE 1 .. LENGTH(V_PROCESSO)
LOOP
V_SOMA := TO_CHAR (V_SOMA) + TO_NUMBER(SUBSTR(V_PROCESSO,i,1))*V_PESO;
IF V_PESO = 9 THEN --repetir peso se for necessario
V_PESO := 2;
ELSE
V_PESO := V_PESO + 1;
END IF;
END LOOP;
V_RESTO := MOD(V_SOMA, 11);
Result := 11 - V_RESTO;
IF ((Result = 0) OR (Result = 1) OR (Result >= 10)) THEN
Result := 1;
END IF;
END;
return(Result);
end FNC_CALCULATE_MOD11;
Try to change V_PROCESSO to a bigger size, for example V_PROCESSO VARCHAR2(300);

Oracle how to instr for tab or end of line using regexp_instr

I have the following code
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
l_field_start := l_field_end + 1;
exit when l_field_end = 0;
end loop;
dbms_output.put_line('');
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;
with which I'm trying to parse this clob test data:
LINE_TEXT
"PERSON_ID_NUMBER 30000 1223445454"
"PERSON_DOB 30000 01-01-1900"
The clob data is tab separated and has a chr(10) at the end. I'm not familiar with regexp_instr, but currently I'm only using instr to search for the tab separators; so it's missing the end of line field and producing:
PERSON_ID_NUMBER,30000,,
PERSON_DOB,30000,,
How can I change the instr into a regexp_instr to also look for the end of line character in addition to the tab and then correctly pick up the last field?
I need the function to be performant, since it is parsing large files.
You can split the line of CLOB column by converting to char, and then apply regexp_substr() as
with t as
(
select level as row_num, regexp_substr(to_char(t.line_text),'^.*$',1,level,'m') as str
from tab t
connect by level <= length (to_char(t.line_text))
- length (replace (to_char(t.line_text), chr (10))) + 1
)
select row_num, regexp_replace(str,'[[:space:]]+',',') as str
from t;
ROW_NUM STR
------- -----------------------------------------
1 PERSON_ID_NUMBER,30000,1223445454
2 PERSON_DOB,30000,01-01-1900
Demo
Edit : even works without to_char() conversion, if your CLOB is huge then you need to split step by step by substr(str,1,4000), substr(str,4001,8000) ...
with t as
(
select level as row_num, regexp_substr(substr(t.line_text,1,4000),'^.*$',1,level,'m') str
from tab t
connect by level <= length (substr(t.line_text,1,4000))
- length (replace(substr(t.line_text,1,4000), chr (10))) + 1
)
select row_num, regexp_replace(substr(str,1,4000),'[[:space:]]+',',') as str
from t
Fixed it with:
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
exit when l_field_end = 0;
l_field_start := l_field_end + 1;
end loop;
l_field := substr(l_line, l_field_start);
dbms_output.put_line(l_field);
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;

How to find the length of an input string without using standard length function in PL/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

Unscramble string code in sql

I have a function in Oracle SQL which scrambles a given input. I need to unscramble it.
I need a program which can basically reverse the output of the program below.
source IN VARCHAR2
sGARBLED VARCHAR2(510);
index NUMBER;
length NUMBER;
onec NUMBER;
BEGIN
length := LENGTH(source);
IF length > 255 THEN
length := 255;
END IF;
index := 1;
sGARBLED := '';
WHILE index <= length LOOP
onec := ASCII(SUBSTR(source, index, 1)) - 30;
IF (onec < 10) THEN
sGARBLED := sGARBLED || '0';
END IF;
sGARBLED := sGARBLED || CAST(onec AS VARCHAR2);
index := index + 1;
END LOOP;
RETURN sGARBLED;
END
It can be possible, if you are only allowing character values in your original string before scrambling it, to be characters of the alphabet.
Code would look something like this:
sgarbled in VARCHAR2
UNsGARBLED VARCHAR2(255);
length NUMBER;
index NUMBER;
onec NUMBER;
BEGIN
length := LENGTH(sgarbled);
index := 1;
UNsGARBLED := '';
WHILE index <= length LOOP
onec := SUBSTR(sgarbled, index, 1);
IF (onec = '0') THEN
onec := SUBSTR(sgarbled, (index + 1), 1) + 30;
ELSE
onec := SUBSTR(sgarbled, (index), 2) + 30;
END IF;
UNsGARBLED := UNsGARBLED || CAST(CHR(onec) AS VARCHAR2);
index := index + 2;
END LOOP;
RETURN UNsGARBLED;
END
As Mr. Llama pointed out if you are allowing the original input string to contain all ASCII characters then it may not be possible.

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;