Converting number into word using length PL/SQL - oracle

I am converting number into word with own code using length function.
Here is the code:
CREATE OR REPLACE FUNCTION num_to_words (NUM IN NUMBER)
RETURN VARCHAR2
IS
v_length NUMBER;
v_words VARCHAR2(100);
word_1 VARCHAR2(100);
BEGIN
word_1 := 'HUNDRED';
v_length := LENGTH(NUM);
IF v_length = 3
THEN v_words := SUBSTR(TO_CHAR(TO_DATE(NUM,'J'),'JSP'),1,3) ||' '|| word_1;
END IF;
RETURN(v_words);
END;
Problem is when I enter "100", it successfully converts into "ONE HUNDRED".
How can I implement the code for converting "101" into "ONE HUNDRED ONE".

The JSP conversion will do that for you; you don't need to extract the first digit, or supply the 'hundred' text yourself:
IF v_length <= 3 THEN
v_words := TO_CHAR(TO_DATE(NUM,'J'),'JSP');
END IF;
If you pass in 101 the result is ONE HUNDRED ONE. And because I changed = 3 to <= 3 it will work for any 1, 2 or 3-digit value, so passing in 11 returns ELEVEN.
For longer values you might be looking for something like this, which breaks the number down into groups and converts each group onto words plus the corresponding scale (e.g. 'million'). If you want non-standard grouping then it's a bit more involved, but there's a recent example for lakh here.

Related

How to replace comma separated different values with a particular value

I've a string with the values - 'a','b','c','d,'e'. I need to convert each of the values to the text null - null,null,null,null,null. If there are 10 values in quotes and separated by comma, then 10 null will appear. I tried using REGEXP_REPLACE but failed to get the result.
declare
a varchar2(32767) := q'#'a','b','c'#';
c varchar2(32767);
begin
c := replace(REGEXP_REPLACE (a, <don't know what pattern should be here>, 'null'),'''');
dbms_output.put_line(c);
end;
/
There are many ways to do this. For example, you could use the regular expression '[^,]+' - which means any number (one or more) of consecutive non-comma characters. Then every occurrence of that pattern will be replaced with the replacement string (while the commas will stay in place).
declare
a varchar2(32767) := q'#'a','b','c'#';
c varchar2(32767);
begin
c := REGEXP_REPLACE (a, '[^,]+', 'null'); -- notice the regular expression
dbms_output.put_line(c);
end;
/
null,null,null
PL/SQL procedure successfully completed.

Accessing elements of Oracle PLSQL record type on runtime

I am using dynamic SQL where I am dynamically using value of column name to bind and its value to be bind
OLD CODE
<Outer Loop>
FOR i IN lvaDBOBJDTLRecTab.FIRST .. lvaDBOBJDTLRecTab.LAST
LOOP
DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId, ':RTTEXT2VC100',
lvaDBOBJDTLRecTab(i).DBONAME );
DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId, ':RTTEXT3VC100',
lvaDBOBJDTLRecTab(i).DBOTYPE );
3.
.
.
.
100
END LOOP;
Instead of writing BIND_VARIABLE for 100 times , I want to dynamically access value of collection. I am able to fetch the value of columns dynamically, which need to be bind (lvsColForBinding), however value of lvsColValForBind
is coming as 'lvrCurDBOBJDTL(i).DBONAME' , 'lvrCurDBOBJDTL(i).DBOTYPE'
and same for rest of the 98 columns,
<Inner Loop>
FOR j IN lvaMappingTab.FIRST..lvaMappingTab.LAST
LOOP
lvsColForBinding := ':'||lvaMappingTab(j).MstRptColCds;
lvsColValForBind := 'lvrCurDBOBJDTL(i).'||lvaMappingTab(j).RptColCd;
DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId,lvsColForBinding, lvsColValForBind);
END LOOP;
when DBMS_SQL.BIND_VARIABLE is run for each row, as mentioned earlier Column to be bind comes correct but value to be bind, instead of coming as
value of 'XYZ' = lvrCurDBOBJDTL(i).DBONAME it comes as this in single quotes 'lvrCurDBOBJDTL(i).DBONAME' same for all columns.
how can we extract the value of each element in inner loop. what step we need to do to fetch the value of lvsColValForBind?
While debugging through SQLDEveloper Watches, I can see the, element name, value and type, when adding and double clicking the plsql record variable,
what is the SQL behind that, can we use that in coding ?
My first recommendation is that you use dynamic SQL to generate lots of dumb code instead of using a small amount of smart PL/SQL. If code generation doesn't work, you can use ANYDATA and ANYTYPE to create PL/SQL reflection to dynamically iterate through the elements of a record at run time.
Code Generation
Don't write BIND_VARIABLE 100 times, but create a small program to generate the 100 lines of code for you. If the data is ultimately coming from one table and going into another table, the input and output may be predictable based on data dictionary views like DBA_TAB_COLUMNS.
Hopefully a small query like this could help generate all the code for a single table:
--Generate PL/SQL statements for binds.
select
'DBMS_SQL.BIND_VARIABLE(lvnInsertCursorId, '':RTTEXT'||column_id||'VC100'', lvaDBOBJDTLRecTab(i).'||column_name||');'
from dba_tab_columns
where owner = 'SOME_OWNER'
and table_name = 'SOME_TABLE'
order by 1;
Then you can copy-and-paste the output into the PL/SQL block. You'll probably also want a warning, like "do not modify, this code is autogenerated by the procedure CODE_TRON_2000".
This approach will only work if the PL/SQL code is predictable, based on the data dictionary or some other metadata.
PL/SQL Reflection
There's no pure PL/SQL reflection for PL/SQL types* but there's a simple workaround if you're willing to create the record types as SQL objects instead. If all your PL/SQL records are based on object types then ANYDATA and ANYTYPE can be used to dynamically access attributes. Object types and PL/SQL record types are pretty similar, it should be relatively painless to convert one to the other.
For example, if you create an object type that contains a number and a string:
create or replace type v_type is object(a number, b varchar2(1));
This (painful) PL/SQL block shows how to iterate through all the records of a collection, and then iterate through all of the attributes in each record. (The code prints the values for, you'll have to add the binding parts yourself.)
declare
type v_nt_type is table of v_type;
v_values v_nt_type := v_nt_type(v_type(1, 'A'), v_type(2, 'B'));
begin
--For each record:
for i in 1 .. v_values.count loop
declare
v_anydata anydata := anydata.ConvertObject(v_values(i));
v_number number;
v_varchar2 varchar2(4000);
v_result pls_integer;
v_anytype anytype;
v_dummy_num pls_integer;
v_dummy_char varchar2(4000);
v_dummy_anytype anytype;
v_number_of_elements number;
begin
--Get the ANYTYPE and the number of elements.
v_result := v_anydata.getType(v_anytype);
v_result := v_anytype.getInfo
(
prec => v_dummy_num,
scale => v_dummy_num,
len => v_dummy_num,
csid => v_dummy_num,
csfrm => v_dummy_num,
schema_name => v_dummy_char,
type_name => v_dummy_char,
version => v_dummy_char,
numelems => v_number_of_elements
);
--For each element in the record:
for i in 1 .. v_number_of_elements loop
--Find the type of the element:
v_anydata.piecewise;
v_result := v_anytype.getAttrElemInfo(
pos => i,
prec => v_dummy_num,
scale => v_dummy_num,
len => v_dummy_num,
csid => v_dummy_num,
csfrm => v_dummy_num,
attr_elt_type => v_dummy_anytype,
aname => v_dummy_char);
--This is where you do something interesting with the values.
--(The same code merely prints the values.)
if v_result = dbms_types.typecode_number then
v_result := v_anydata.getNumber(num => v_number);
dbms_output.put_line(v_number);
elsif v_result = dbms_types.typecode_varchar2 then
v_result := v_anydata.getVarchar2(c => v_varchar2);
dbms_output.put_line(v_varchar2);
--TODO: Add other potential types here.
end if;
end loop;
end;
end loop;
end;
/
Results:
1
A
2
B
* You're right that there must be some way to find this run time information, if the debugger gets it. But as far as I know there is no way for PL/SQL to retrieve that debug information. Maybe it's only available to an OCI(?) interface?
When you call bind_variable, you are binding an actual value to a placeholder. So if you provide a string that is the name of your variable, well, that string is the value bound to the placeholder.
If the array holds those values, then simply reference the array element and not the name of that element, as in:
DBMS_SQL.BIND_VARIABLE (
lvnInsertCursorId,
lvaMappingTab(j).MstRptColCds,
lvrCurDBOBJDTL(i).lvaMappingTab(j).RptColCd);
But I am pretty sure that's not what you've got there. Hope this helps!
#Jon
Thanks for your inputs, that helped. also I am able to iterate cols without creating OBJECTs using DBMS_SQL.DESCRIBE_COLUMNS.
**Below code still need little bit fine tuning, but mostly working :)
BEGIN
COLS_TRAVERSE('SELECT * FROM ALL_OBJECTS WHERE ROWNUM<=100');
END;
create or replace PROCEDURE COLS_TRAVERSE ( p_query in varchar2 )
AS
v_curid NUMBER;
v_desctab DBMS_SQL.DESC_TAB;
v_colcnt NUMBER;
v_RowNumcnt NUMBER := 1;
v_Colname_var VARCHAR2(10000);
v_name_var VARCHAR2(10000);
v_num_var NUMBER;
v_date_var DATE;
v_row_num NUMBER;
p_sql_stmt VARCHAR2(1000);
BEGIN
v_curid := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_curid, p_query, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS(v_curid, v_colcnt, v_desctab);
-- Define columns:
FOR i IN 1 .. v_colcnt LOOP
IF v_desctab(i).col_type = 2 THEN
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_num_var);
ELSIF v_desctab(i).col_type = 12 THEN
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_date_var);
ELSE
DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_name_var, 50);
END IF;
END LOOP;
v_row_num := dbms_sql.execute(v_curid);
-- Fetch rows with DBMS_SQL package:
WHILE DBMS_SQL.FETCH_ROWS(v_curid) > 0 LOOP
FOR i IN 1 .. v_colcnt
LOOP
v_Colname_var := v_desctab(i).col_name;
dbms_output.put_line( 'Name:' ||v_Colname_var );
IF (v_desctab(i).col_type = 1) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_name_var);
dbms_output.put_line( 'String Value:' || v_name_var );
ELSIF (v_desctab(i).col_type = 2) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_num_var);
dbms_output.put_line( 'Number Value:' || v_num_var);
ELSIF (v_desctab(i).col_type = 12) THEN
DBMS_SQL.COLUMN_VALUE(v_curid, i, v_date_var);
dbms_output.put_line( 'Date Value:' || v_date_var );
END IF;
END LOOP;
dbms_output.put_line( 'End of Row Number # ' ||v_RowNumcnt );
v_RowNumcnt := v_RowNumcnt+1;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(v_curid);
END;
/
DBMS_OUT PUT
Name:OWNER
String Value:SYS
Name:OBJECT_NAME
String Value:ORA$BASE
Name:SUBOBJECT_NAME
String Value:
Name:OBJECT_ID
Number Value:134
Name:DATA_OBJECT_ID
Number Value:
Name:OBJECT_TYPE
String Value:EDITION
Name:CREATED
Date Value:30-03-18
Name:LAST_DDL_TIME
Date Value:30-03-18
Name:TIMESTAMP
String Value:2018-03-30:21:37:22
Name:STATUS
String Value:VALID
Name:TEMPORARY
String Value:N
Name:GENERATED
String Value:N
Name:SECONDARY
String Value:N
Name:NAMESPACE
Number Value:64
Name:EDITION_NAME
String Value:
Name:SHARING
String Value:NONE
Name:EDITIONABLE
String Value:
Name:ORACLE_MAINTAINED
String Value:Y
Name:APPLICATION
String Value:N
Name:DEFAULT_COLLATION
String Value:
Name:DUPLICATED
String Value:N
Name:SHARDED
String Value:N
Name:CREATED_APPID
Number Value:
Name:CREATED_VSNID
Number Value:
Name:MODIFIED_APPID
Number Value:
Name:MODIFIED_VSNID
Number Value:
End of Row Number # 1
Name:OWNER
String Value:SYS
Name:OBJECT_NAME
String Value:DUAL
Name:SUBOBJECT_NAME
String Value:
Name:OBJECT_ID
Number Value:143
Name:DATA_OBJECT_ID
Number Value:143
Name:OBJECT_TYPE
String Value:TABLE
Name:CREATED
Date Value:30-03-18
Name:LAST_DDL_TIME
Date Value:31-03-18
Name:TIMESTAMP
String Value:2018-03-30:21:37:22
Name:STATUS
String Value:VALID
Name:TEMPORARY
String Value:N
Name:GENERATED
String Value:N
Name:SECONDARY
String Value:N
Name:NAMESPACE
Number Value:1
Name:EDITION_NAME
String Value:
Name:SHARING
String Value:METADATA LINK
Name:EDITIONABLE
String Value:
Name:ORACLE_MAINTAINED
String Value:Y
Name:APPLICATION
String Value:N
Name:DEFAULT_COLLATION
String Value:USING_NLS_COMP
Name:DUPLICATED
String Value:N
Name:SHARDED
String Value:N
Name:CREATED_APPID
Number Value:
Name:CREATED_VSNID
Number Value:
Name:MODIFIED_APPID
Number Value:
Name:MODIFIED_VSNID
Number Value:
End of Row Number # 2

Shortname function not working properly

There is this code for generatig unique shortname from a table MMSTREPHDR .
I have shortnames kv,kv1,kv2,kv3 already in MMSTREPHDR . But on passing parameter kv it gives me kv1 and not kv4(since it's in LOOP) . Can't figure out what's wrong ?
FUNCTION FUN_GENERATE_SNAME (p_name VARCHAR2)
RETURN VARCHAR2
IS
vl_sname VARCHAR2 (15);
n_cnt NUMBER := 1;
vl_sub NUMBER;
CURSOR c1 (vl_sname VARCHAR2)
IS
SELECT a.repsname, a.repcode
FROM MMSTREPHDR a
WHERE TRIM (UPPER (a.repsname)) = TRIM (UPPER (vl_sname));
BEGIN
vl_sname := TRIM (SUBSTR (p_name, 1, 15));
FOR i IN c1 (vl_sname)
LOOP
vl_sub := LENGTH (TO_CHAR (n_cnt));
vl_sname := SUBSTR (vl_sname, 1, (15 - vl_sub)) || n_cnt;
n_cnt := n_cnt + 1;
END LOOP;
RETURN vl_sname;
EXCEPTION
WHEN OTHERS
THEN
RETURN vl_sname;
END fun_generate_sname;
Your starting parameter is 'kv'. This is what you pass to the cursor. Consequently your cursor will select one row , the row where MMSTREPHD.repsname = 'kv'.
So your loop logic will be executed once. So cnt = . Hencevl_sname` becomes 'kv1', which is the value you get when the loop exits cleanly.
The cleanest way to fix this would be to admit that MMSTREPHD.repsname is a smart key, consisting of two elements: a subsystem name and a report number. Splitting the column into two columns would make it a cinch to find the next report number for a given sub-system. You can even retain the composite value as a virtual column (11g or later), or maintain it with triggers which sucks a bit.
Otherwise:
select concat(p_name
, trim(to_char(max(to_number(nvl(replace(repsname,p_name),'0')))+1)) )
into vl_sname
from MMSTREPHD
where repsname like p_name||'%'
Caveat - I haven't tested this (yet) so ths brackets may not pair up correctly.
You are concatenating the in below statement as
vl_sname := SUBSTR (vl_sname, 1, (15 - vl_sub)) || n_cnt;
here n_cnt is given initial value to it as 1 in code above thats why its fetching you value as KV1.You should keep it null and after statement should increment it by 1 in loop. Hope will be usefull
try this function
function FUN_GENERATE_SNAME(p_name varchar2) return varchar2 is
l_idx number;
l_name_ln := length(trim(p_name));
begin
select max(substr(trim(UPPER(a.repsname)), 1, -length(trim(UPPER(a.repsname)) + l_name_ln))
into l_idx
from MMSTREPHDR a
where substr(trim(UPPER(a.repsname)), 1, l_name_ln) = trim(UPPER(vl_sname));
if l_idx is null then
-- mean name is unique
return vl_sname;
else
return vl_sname ||(l_idx + 1);
end if;
exception
when others then
return vl_sname;
end fun_generate_sname;

How to generate a version 4 (random) UUID on Oracle?

This blog explains, that the output of sys_guid() is not random for every system:
http://feuerthoughts.blogspot.de/2006/02/watch-out-for-sequential-oracle-guids.html
Unfortunately I have to use such a system.
How to ensure to get a random UUID? Is it possible with sys_guid()? If not how to reliably get a random UUID on Oracle?
Here's a complete example, based on #Pablo Santa Cruz's answer and the code you posted.
I'm not sure why you got an error message. It's probably an issue with SQL Developer. Everything works fine when you run it in SQL*Plus, and add a function:
create or replace and compile
java source named "RandomUUID"
as
public class RandomUUID
{
public static String create()
{
return java.util.UUID.randomUUID().toString();
}
}
/
Java created.
CREATE OR REPLACE FUNCTION RandomUUID
RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'RandomUUID.create() return java.lang.String';
/
Function created.
select randomUUID() from dual;
RANDOMUUID()
--------------------------------------------------------------
4d3c8bdd-5379-4aeb-bc56-fcb01eb7cc33
But I would stick with SYS_GUID if possible. Look at ID 1371805.1 on My Oracle Support - this bug is supposedly fixed in 11.2.0.3.
EDIT
Which one is faster depends on how the functions are used.
It looks like the Java version is slightly faster when used in SQL. However, if you're going to use this function in a PL/SQL context, the PL/SQL function is about
twice as fast. (Probably because it avoids overhead of switching between engines.)
Here's a quick example:
--Create simple table
create table test1(a number);
insert into test1 select level from dual connect by level <= 100000;
commit;
--SQL Context: Java function is slightly faster
--
--PL/SQL: 2.979, 2.979, 2.964 seconds
--Java: 2.48, 2.465, 2.481 seconds
select count(*)
from test1
--where to_char(a) > random_uuid() --PL/SQL
where to_char(a) > RandomUUID() --Java
;
--PL/SQL Context: PL/SQL function is about twice as fast
--
--PL/SQL: 0.234, 0.218, 0.234
--Java: 0.52, 0.515, 0.53
declare
v_test1 raw(30);
v_test2 varchar2(36);
begin
for i in 1 .. 10000 loop
--v_test1 := random_uuid; --PL/SQL
v_test2 := RandomUUID; --Java
end loop;
end;
/
Version 4 GUIDs are not completely random. Some of the bytes are supposed to be fixed. I'm not sure why this was done, or if it matters, but according to https://www.cryptosys.net/pki/uuid-rfc4122.html:
The procedure to generate a version 4 UUID is as follows:
Generate 16 random bytes (=128 bits)
Adjust certain bits according to RFC 4122 section 4.4 as follows:
set the four most significant bits of the 7th byte to 0100'B, so the high nibble is "4"
set the two most significant bits of the 9th byte to 10'B, so the high nibble will be one of "8", "9", "A", or "B".
Encode the adjusted bytes as 32 hexadecimal digits
Add four hyphen "-" characters to obtain blocks of 8, 4, 4, 4 and 12 hex digits
Output the resulting 36-character string "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
The values from the Java version appear to conform to the standard.
https://stackoverflow.com/a/10899320/1194307
The following function use sys_guid() and transform it into uuid format:
create or replace function random_uuid return VARCHAR2 is
v_uuid VARCHAR2(40);
begin
select regexp_replace(rawtohex(sys_guid()), '([A-F0-9]{8})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{12})', '\1-\2-\3-\4-\5') into v_uuid from dual;
return v_uuid;
end random_uuid;
It do not need create dbms_crypto package and grant it.
I use this now as a workaround:
create or replace function random_uuid return RAW is
v_uuid RAW(16);
begin
v_uuid := sys.dbms_crypto.randombytes(16);
return (utl_raw.overlay(utl_raw.bit_or(utl_raw.bit_and(utl_raw.substr(v_uuid, 7, 1), '0F'), '40'), v_uuid, 7));
end random_uuid;
The function requires dbms_crypto and utl_raw. Both require an execute grant.
grant execute on sys.dbms_crypto to uuid_user;
The easiest and shortest way to get a Java-based function for me was:
create or replace function random_uuid return varchar2 as
language java
name 'java.util.UUID.randomUUID() return String';
I can't completely understand why it does not compile if I add .toString() though.
I wasn't fully satisfied with any of the above answers: Java is often not installed, dbms_crypto needs a grant and sys_guid() is sequential.
I settled on this.
create or replace function random_uuid return VARCHAR2 is
random_hex varchar2(32);
begin
random_hex := translate(DBMS_RANDOM.string('l', 32), 'ghijklmnopqrstuvwxyz', '0123456789abcdef0123');
return substr(random_hex, 1, 8)
|| '-' || substr(random_hex, 9, 4)
|| '-' || substr(random_hex, 13, 4)
|| '-' || substr(random_hex, 17, 4)
|| '-' || substr(random_hex, 21, 12);
end random_uuid;
/
dbms_random is (by default) public so no grants are required and it is reasonably random. Note that dbms_random is not cryptographically secure so if you need that, use the dbms_crypto approach above. The hex value distribution is also skewed by the translate function.
If you need real UUID 4 output then you can tweak the substr (I just need uniqueness).
The same technique could be used in sql without a function with some imagination:
select
substr(rand, 1, 8)
|| '-' || substr(rand, 9, 4)
|| '-' || substr(rand, 13, 4)
|| '-' || substr(rand, 17, 4)
|| '-' || substr(rand, 21, 12)
from (select translate(DBMS_RANDOM.string('l', 32), 'ghijklmnopqrstuvwxyz', '0123456789abcdef0123') rand from dual);
You can write a Java procedure and compile it and run it inside Oracle. In that procedure, you can use:
UUID uuid = UUID.randomUUID();
return uuid.toString();
To generate desired value.
Here's a link on how to compile java procedures in Oracle.
It may not be unique, but generate a "GUID-like" random string:
FUNCTION RANDOM_GUID
RETURN VARCHAR2 IS
RNG NUMBER;
N BINARY_INTEGER;
CCS VARCHAR2 (128);
XSTR VARCHAR2 (4000) := NULL;
BEGIN
CCS := '0123456789' || 'ABCDEF';
RNG := 15;
FOR I IN 1 .. 32 LOOP
N := TRUNC (RNG * DBMS_RANDOM.VALUE) + 1;
XSTR := XSTR || SUBSTR (CCS, N, 1);
END LOOP;
RETURN XSTR;
END RANDOM_GUID;
Adapted from source of DBMS_RANDOM.STRING.
According to UUID Version 4 format should be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx. #lonecat answer provide this format, also #ceving answer partially provide version 4 requirements. Missing part is format y, y should be one of 8, 9, a, or b.
After mixing these answers and fix the y part, code looks like below:
create or replace function fn_uuid return varchar2 is
/* UUID Version 4 must be formatted as xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal character (lower case only) and y is one of 8, 9, a, or b.*/
v_uuid_raw raw(16);
v_uuid varchar2(36);
v_y varchar2(1);
begin
v_uuid_raw := sys.dbms_crypto.randombytes(16);
v_uuid_raw := utl_raw.overlay(utl_raw.bit_or(utl_raw.bit_and(utl_raw.substr(v_uuid_raw, 7, 1), '0F'), '40'), v_uuid_raw, 7);
v_y := case round(dbms_random.value(1, 4))
when 1 then
'8'
when 2 then
'9'
when 3 then
'a'
when 4 then
'b'
end;
v_uuid_raw := utl_raw.overlay(utl_raw.bit_or(utl_raw.bit_and(utl_raw.substr(v_uuid_raw, 9, 1), '0F'), v_y || '0'), v_uuid_raw, 9);
v_uuid := regexp_replace(lower(v_uuid_raw), '([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{12})', '\1-\2-\3-\4-\5');
return v_uuid;
end fn_uuid;
Accepted answer from ceving is inconsistent with RFC4122: the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved should be set to zero and one, respectively. That makes y equal to 8,9,a or b in already mentioned by uğur-yeşilyurt format xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
My solution made point blank along RFC:
create or replace function random_uuid return raw is
/*
Set the four most significant bits (bits 12 through 15) of the
time_hi_and_version field to the 4-bit version number from
Section 4.1.3.
*/
v_time_hi_and_version raw(2) := utl_raw.bit_and(utl_raw.bit_or(dbms_crypto.randombytes(2), '4000'), '4FFF');
/*
Set the two most significant bits (bits 6 and 7) of the
clock_seq_hi_and_reserved to zero and one, respectively.
*/
v_clock_seq_hi_and_reserved raw(1) := utl_raw.bit_and(utl_raw.bit_or(dbms_crypto.randombytes(1), '80'), 'BF');
/*
Set all the other bits to randomly (or pseudo-randomly) chosen
values.
*/
v_time raw(6) := dbms_crypto.randombytes(6);
v_clock_seq_low_and_node raw(7) := dbms_crypto.randombytes(7);
begin
return v_time || v_time_hi_and_version || v_clock_seq_hi_and_reserved || v_clock_seq_low_and_node;
end random_uuid;
EDIT:
Although first implementation easy to understand it's rather inefficient. Next solution is 3 to 4 times faster.
create or replace function random_uuid2 return raw is
v_uuid raw(16) := dbms_crypto.randombytes(16);
begin
v_uuid := utl_raw.bit_or(v_uuid, '00000000000040008000000000000000');
v_uuid := utl_raw.bit_and(v_uuid, 'FFFFFFFFFFFF4FFFBFFFFFFFFFFFFFFF');
return v_uuid;
end;
This test demostrates that random_uuid takes about one millisecond and random_uuid2 only 250 microseconds. Concatenation in the first version consumed too much time;
declare
dummy_uuid raw(16);
begin
for i in 1 .. 20000 loop
--dummy_uuid := random_uuid;
dummy_uuid := random_uuid2;
end loop;
end;
there are some pure plsql functions written by me and one of my friend that generates uuid version 4 and formats any type of GUIDs. also formatters written in two way. one concating string and one use regex for formatting uuid
CREATE OR REPLACE FUNCTION RANDOM_UUD_RAW
RETURN RAW IS V_UUID RAW(16);
BEGIN V_UUID := SYS.DBMS_CRYPTO.Randombytes(16);
V_UUID := UTL_RAW.Overlay(UTL_RAW.Bit_or(UTL_RAW.Bit_and(UTL_RAW.Substr(V_UUID, 7, 1), '0F'), '40'), V_UUID, 7, 1);
V_UUID := UTL_RAW.Overlay(UTL_RAW.Bit_or(UTL_RAW.Bit_and(UTL_RAW.Substr(V_UUID, 9, 1), '3F'), '80'), V_UUID, 9, 1);
RETURN V_UUID;
END RANDOM_UUD_RAW; --
CREATE OR REPLACE FUNCTION UUID_FORMATTER_CONCAT(V_UUID RAW)
RETURN VARCHAR2 IS V_STR VARCHAR2(36);
BEGIN V_STR := lower(SUBSTR(V_UUID, 1, 8) || '-' || SUBSTR(V_UUID, 9, 4) || '-' || SUBSTR(V_UUID, 13, 4) || '-' || SUBSTR(V_UUID, 17, 4) || '-' || SUBSTR(V_UUID, 21));
RETURN V_STR;
END UUID_FORMATTER_CONCAT; --
CREATE OR REPLACE FUNCTION UUID_FORMATTER_REGEX(V_UUID RAW)
RETURN VARCHAR2 IS V_STR VARCHAR2(36);
BEGIN V_STR := lower(regexp_replace(V_UUID, '(.{8})(.{4})(.{4})(.{4})(.{12})', '\1-\2-\3-\4-\5'));
RETURN V_STR;
END UUID_FORMATTER_REGEX; --
CREATE OR REPLACE FUNCTION RANDOM_UUID_STR
RETURN VARCHAR2 AS BEGIN RETURN UUID_FORMATTER_CONCAT(RANDOM_UUD_RAW());
END RANDOM_UUID_STR; --
CREATE OR REPLACE FUNCTION RANDOM_UUID_STR_REGEX
RETURN VARCHAR2 AS BEGIN RETURN UUID_FORMATTER_REGEX(RANDOM_UUD_RAW());
END RANDOM_UUID_STR_REGEX;

Concatenation with Zero is not occurring properly in code?

I was trying to reverse a number in PL/SQL. It's working fine, but when my number contains any 0, the output is unexpected. For example:
1234 output 4321
1000 output 1
1203 ouput 3021
10001 output 1
DECLARE
r number(9);
num number(9):=&p_num;
BEGIN
WHILE num>=1 LOOP
IF mod(num,10)=0 THEN -- extracting last digit of a number .
r:=r||0; --if end digit is 0 then concat r with 0
ELSE
r:=r||mod(num,10);--if mod is not 0 then it would be last digit.
END IF;
num:=trunc(num/10);--Removing last digit from number
END LOOP;
dbms_output.put_line(r);
END;
Try changing the type of the variable "r" to varchar2.
Since it's declared as number, leading zeros are discarded.
'Reversing a number' is fundamentally a string operation, not a numerical one. Numerically, the reverse of 10, 100, 1000, etc are all 1 - the leading zeroes in the result don't count. And the operation is not, therefore, invertible; all numbers with the same leading (significant) digits and with zero or more trailing zeroes produce the same output when reversed.
So, you need to revise your code to generate a character string, not a number.
You can't preserve leading zeros with numbers; you must use strings (varchar2). Try something like this to see:
DECLARE
r varchar2(9);
num varchars(9):=&p_num;
BEGIN
r := REVERSE(num);
dbms_output.put_line(r);
END;
I'm not sure what's going wrong in your code Vineet but perhaps this will work.
DECLARE
newStr varchar2(9) = "";
numStr varchar2(9) := to_char(&p_num);
i number;
BEGIN
i = length(numStr);
WHILE i>0 LOOP
newStr := newStr || substr(numStr, i, i + 1);
i = i - 1;
END LOOP;
dbms_output.put_line(r);
END;
Edit: Or as gabe correctly points out, just use the REVERSE function.
The problem is you're dealing with a NUMBER value. When you reverse 1000, you get 0001, which when output unformatted is 1.
What you really need is something akin to:
CREATE OR REPLACE FUNCTION rev(p_num NUMBER)
RETURN VARCHAR2 IS
v_chr VARCHAR2(50);
BEGIN
v_chr := p_num;
IF LENGTH(v_chr) > 1 THEN
RETURN SUBSTR(v_chr, -1, 1)||rev(SUBSTR(v_chr, 1, LENGTH(v_chr)-1));
END IF;
RETURN v_chr;
EXCEPTION
WHEN OTHERS THEN
RETURN 'Bad Input';
END;
/
Function created
SQL> SELECT rev(100000) FROM dual;
REV(100000)
--------------------------------------------------------------------------------
000001
SQL>

Resources