Sequences with PL SQL - sql

I know how to create a sequence in pl sql. However, how would I set the values to all have say 3 digits? is there another sql statement to do this when I create a sequence?
so an example would be:
000
001
012
003
Thanks guys!

First, just to be clear, you do not create sequences in PL/SQL. You can only create sequences in SQL.
Second, if you want a column to store exactly three digits, you would need the data type to be VARCHAR2 (or some other string type) rather than the more common NUMBER since a NUMBER by definition does not store leading zeroes. You can, of course, do that, but it would be unusual.
That said, you can use the "fm009" format mask to generate a string with exactly 3 characters from a numeric sequence (the "fm" bit is required to ensure that you don't get additional spaces-- you could TRIM the result of the TO_CHAR call as well and dispense with the "fm" bit of the mask).
SQL> create table t( col1 varchar2(3) );
Table created.
SQL> create sequence t_seq;
Sequence created.
SQL> ed
Wrote file afiedt.buf
1 insert into t
2 select to_char( t_seq.nextval, 'fm009' )
3 from dual
4* connect by level <= 10
SQL> /
10 rows created.
SQL> select * from t;
COL
---
004
005
006
007
008
009
010
011
012
013
10 rows selected.

haven't used plsql in a while, but here goes:
given an integer sequence myseq,
to_char(myseq.nextval, '009')

You can also use the lpad function.
In Oracle/PLSQL, the lpad function pads the left-side of a string with a specific set of characters.
For example:
lpad('tech', 7); would return ' tech'
lpad('tech', 2); would return 'te'
lpad('tech', 8, '0'); would return '0000tech'
lpad('tech on the net', 15, 'z'); would return 'tech on the net'
lpad('tech on the net', 16, 'z'); would return 'ztech on the net'
In your example you would use
lpad('tech', 8, '0'); would return '0000tech'
i.e. if the string is less than 8 characters long, add 0s to the start of the string until the string is 8 characters long.
Ref: http://www.techonthenet.com/oracle/functions/lpad.php
Also, to add the 0s to the right you can use the rpad function.

Related

Masking Data based on Column Value Oracle

I am creating a stored procedure in Oracle where I am required to mask employee ID in the SELECT statement.
I would like that the same masking number be applied to the all the rows with the same Employee ID.
EMP_ID MASK_ID
------ -------
212 USER9293
443 USER6474
212 USER9293
Currently in my MSSQL SELECT I accomplish this by:
CONCAT(‘USER’, CONVERT(NVARCHAR(6), (1.0 + FLOOR(250000 * RAND(CONVERT(VARBINARY, EMP_ID))))))
I would like to accomplish a similar solution in Oracle. I believe a SEED using DBMS_RANDOM would probably get me close but I am not sure how to pass the column in as it return an error every time.
Any tips?
Often people ask for "random" when they don't actually need that. It seems that "hash" should work fine for your problem.
If you want to append a four-digit number (between 1 and 9999, perhaps with leading zeros) to 'USER', you could do something like what I show below. If you want numbers between 1000 and 9999, you can adjust the math yourself.
Note that you may still give a seed (as the third argument to ora_hash) if you need that for some reason. The default seed is 0, so this is deterministic even if you don't specify the seed.
with
inputs (emp_id) as (
select 212 from dual union all
select 443 from dual union all
select 212 from dual union all
select 400 from dual
)
select emp_id,
'USER' || to_char(1 + ora_hash(emp_id, 9998), 'fm0000') as mask_id
from inputs
;
EMP_ID MASK_ID
------ ---------
212 USER8297
443 USER5176
212 USER8297
400 USER2606
1. Use utl_raw.cast_to_raw to convert EMP_ID to binary
2. You must use the plsql block to use DBMS_RANDOM.SEED.
add function my_random_emplID to generate a random number with binary type
create function my_random_emplID(p_seed in number default null)
return number is
begin
if p_seed is not null then
dbms_random.seed(utl_raw.cast_to_raw(p_seed));
end if;
return dbms_random.value;
end;
/
get mask_id
SELECT
CONCAT('USER',CAST(1.0 + FLOOR(250000 * my_random_emplID(1452)) as varchar(6)))
FROM
dual;
demo in db<>fiddle

Oracle returns wrong values with LENGTH and INSTR

I'm aiming to retrieve the position of chars in a string, plus the length of a string.
The value of the notes field in the internal_notes table, for the row with ticket_id equal to 1679467247 is literally 'this is a test note'.
When I use the functions against the literal stirngs they work, but when I retrieve the info directly from the table column, the values are just wrong.
Any ideas as to what might be happening?
select notes,
LENGTH(notes),
INSTR(notes,' ')
FROM internal_notes
where ticket_id = 1679467247
union
select 'this is a test note',
LENGTH('this is a test note'),
INSTR('this is a test note',' ')
from dual
This returns the following:
NOTES LENGTH(NOTES) INSTR(NOTES,' ')
------------------- ------------- ----------------
this is a test note 32 11
this is a test note 19 5
You can get this apparent inconsistency if you have zero-width characters in the value; for example:
create table internal_notes(ticket_id number, notes varchar2(32 char));
insert into internal_notes(ticket_id, notes)
values (1679467247, unistr('\200c\200cthis is a test note\200c\200c\200c\200c\200c\200c\200c\200c\200c\200c\200c'));
insert into internal_notes(ticket_id, notes)
values (1679467248, unistr('\200c\200cthis is a test note'));
insert into internal_notes(ticket_id, notes)
values (1679467249, 'this is a test note');
select notes,
LENGTH(notes),
INSTR(notes,' ')
FROM internal_notes
where ticket_id = 1679467247;
NOTES LENGTH(NOTES) INSTR(NOTES,'')
-------------------------------- ------------- ---------------
‌‌this is a test note‌‌‌‌‌‌‌‌‌‌‌ 32 7
I said 'apparent inconsistency' because those numbers are correct; they just don't look it if you can't see some of the characters. Invisible characters still count.
As #MTO suggested you can use the dump() function to see exactly what is stored in the table, in decimal or hex representation, or mixed with 'normal' characters which can be a bit easier to interpret:
select notes,
LENGTH(notes),
INSTR(notes,' '),
dump(notes, 1000) as dmp
FROM internal_notes;
NOTES LENGTH(NOTES) INSTR(NOTES,'')
-------------------------------- ------------- ---------------
DMP
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
‌‌this is a test note‌‌‌‌‌‌‌‌‌‌‌ 32 7
Typ=1 Len=58: e2,80,8c,e2,80,8c,t,h,i,s, ,i,s, ,a, ,t,e,s,t, ,n,o,t,e,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,8c,e2,80,
8c
‌‌this is a test note 21 7
Typ=1 Len=25: e2,80,8c,e2,80,8c,t,h,i,s, ,i,s, ,a, ,t,e,s,t, ,n,o,t,e
this is a test note 19 5
Typ=1 Len=19: t,h,i,s, ,i,s, ,a, ,t,e,s,t, ,n,o,t,e
db<>fiddle - though that is showing the zero-width characters as question marks, unlike SQL Developer and SQL*Plus.
Other zero-width characters are available (space, non-joiner, joiner), and you might see something different in your dump - it just has to be something your client doesn't display at all. Whatever is in there, if it affects all rows and not just that single ticket, then how and why is probably down to whatever front-end/application populates the table - possibly from a character set mismatch, but it could be intentional. If it is just that ticket then that note is an interesting test...

Oracle convert column which is varchar / hex to numeric format after loading data from xlsx file

I loaded an Excel file into a table and found out that some data in my varchar2 field is in HEX format.
When I execute my query, I have no problem, but when I try to insert my data into another table with a number format it does not work.
This query shows which column is in HEX format :
SELECT qty, TO_NUMBER(REPLACE(qty, CHR(32), '')) as nbkg, RAWTOHEX(qty) as Graphics
FROM (
SELECT nvl(qty, 0) AS qty,
case
when pkg_tools.f_is_number(qty) = 1 then 'OK'
else 'NOK'
end kg
FROM table
)
WHERE kg = 'NOK';
*qty is a varchar2(50)
My output :
qty nbkg Graphics
--- ---- --------
10 009,000 10009,000 3130203030392C303030 -- work
3 250,00 3250,00 33203235302C3030 -- work
1 000,00 1000,00 31203030302C3030 -- work
1 230,00 1 230,00 31A03233302C3030 -- Not work
1 750,00 1 750,00 31A03735302C3030 -- Not work
4 000,00 4 000,00 34A03030302C3030 -- Not work
1 980,00 1 980,00 31A03938302C3030 -- Not work
1 050,00 1 050,00 31A03035302C3030 -- Not work
1 050,00 1 050,00 31A03035302C3030 -- Not work
1 000,00 1 000,00 31A03030302C3030 -- Not work
39 950,00 39 950,00 3339A03935302C3030 -- Not work
3 000,00 3 000,00 33A03030302C3030
...
...
I am trying to convert it into a number before inserting my data :
SELECT TO_NUMBER(REPLACE(qty, CHR(32), ''))
FROM table;
SELECT TO_NUMBER(REGEXP_REPLACE(qty, '\s'))
FROM table;
and I am getting an error :
ORA-01722: invalid number
How can i convert this column which is varchar / hex to numeric format?
Thank you.
add a Format mask and NLS information by using to_number function.
so it could look like:
select to_number('1 250,000','999G999G999D99999','NLS_NUMERIC_CHARACTERS='', ''') as n
from dual
if you check the hex value e.g. 31A03030302C3030 you can see the A0 on the second Position. that is displayed as empty string but is and not a space that has a hex Position 20 in ASCII table. So just replace that 160 with 32
to_number(replace('1 250,000',chr(160),chr(32)),'999G999G999D99999','NLS_NUMERIC_CHARACTERS='', ''') as n
result:
n
------
1250

Can (should) I include elements of TO_CHAR, CONCAT and FM in the same select statement?

I'm following up on a previous post that successfully generated an Oracle SELECT statement. From within that prior script I now need to
concatenate two different fields (numeric values for 3-digit area codes and 7-digit phone numbers), then
format the resulting column as XXX-XXX-XXXX
but my attempts at using TO_CHAR, CONCAT (or || I have tried doing the concatenation both ways), and FM in the same line result in invalid number or invalid operator errors (depending on how I've rearranged the elements in the line) painfully reminding me that my barely-elementary scripting shows a significant lack understanding of proper use and syntax.
The combination of TO_CHAR and CONCAT (||) successfully produces a 9-digit string, but I'm trying to attain as result formatted as XXX-XXX-XXXX from the following (I've edited out the lines from the original script for data elements not relevant to this particular question; nothing in the original query is nested, it just selects several fields and has a series of left joins linking on a common UID field in different tables)
select distinct
cn.dflt_id StudentIdNumber,
to_char (p.area_code || p.phone_no) Phone,
from
co_name cn
left join co_v_name_phone1 p on cn.name_id = p.name_id
order by cn.dflt_id
Would anyone offer helpful advice on attaining the desired XXX-XXX-XXXX formatting in the resulting Phone column? My attempts with variants of 'fm999g999g9999' have thus far not been successful.
Thanks,
Scott
Here are a few options that crossed my mind; have a look, pick the one you find the most appropriate. If you still have problems, post your own test case.
RES2 is a simple concatenation of substrings that have a - in between
RES3 uses format mask with an adjusted NLS_NUMERIC_CHARACTERS for thousands
RES4 concatenates area code (which is OK by itself) with regular expression that splits a string into two parts; the first has {3} characters, and the second one has {4} of them
By the way, are area codes really numbers? No leading zeros?
SQL> with test (area_code, phone_number) as
2 (select 123, 9884556 from dual union
3 select 324, 1254789 from dual
4 )
5 select
6 to_char(area_code) || to_char(phone_number) l_concat,
7 --
8 substr(to_char(area_code) || to_char(phone_number), 1, 3) ||'-'||
9 substr(to_char(area_code) || to_char(phone_number), 4, 3) ||'-'||
10 substr(to_char(area_code) || to_char(phone_number), 7)
11 res2,
12 --
13 to_char(to_char(area_code) || to_char(phone_number),
14 '000g000g0000', 'nls_numeric_characters=.-') res3,
15 --
16 to_char(area_code) ||'-'||
17 regexp_replace(to_char(phone_number), '(\d{3})(\d{4})', '\1-\2') res4
18 from test;
L_CONCAT RES2 RES3 RES4
------------- ------------- ------------- -------------
1239884556 123-988-4556 123-988-4556 123-988-4556
3241254789 324-125-4789 324-125-4789 324-125-4789
SQL>

Insert character to a number datatype column

Note: I cant change the datatype of column
I want to store a character into a table that has column with number datatype.
The work around i found is convert the character values to ASCII and when retrieving it from the database convert it back to character.
I used couple of function ASCII and ASCIISTR but the limitation with these functions are they are converting only first character of the string.
So i used dump function
select dump('Puneet_kushwah1') from dual;
Result: Typ=96 Len=15: 80,117,110,101,101,116,95,107,117,115,104,119,97,104,49
This function is giving ASCII value of all the characters. Then i execute below query
select replace(substr((DUMP('Puneet_kushwah1')),(instr(DUMP('Puneet_kushwah1'),':')+2 )),',',' ') from dual;
Result: 80 117 110 101 101 116 95 107 117 115 104 119 97 104 49
then i used a special character to fill the space, so that i can replace it while retrieving from the database.
select replace(substr((DUMP('Puneet_kushwah1')),(instr(DUMP('Puneet_kushwah1'),':')+2 )),',','040') from dual;
Result: 80040117040110040101040101040116040950401070401170401150401040401190409704010404049
Table definition:
create table test (no number);
Then i inserted it into the table
INSERT into test SELECT replace(substr((DUMP('Puneet_kushwah1')),(instr(DUMP('Puneet_kushwah1'),':')+2 )),',','040') from dual;
Problem 1:
When i execute
select * from test;
i got
Result: 8.004011704011E82
I want to convert it into number only. Exact same what i inserted.
Problem 2:
And then when i execute select i want it to return the exact character string.
Please help i tried many functions.
Thanks in advance.
You can't get the exact string back because Oracle numbers are only stored up to 38 digits of precision.
So if you run this:
select cast(no as varchar2(100))
from test;
You'll get:
80040117040110040101040101040116040950400000000000000000000000000000000000000000000
While I advise not to proceed like this as this could be rife for errors and a possible maintenance nightmare, I do like a challenge and have been forced to do some screwy things myself in order make some vendor's bizarre way of doing things work for us so I sympathize with you if that is the case. So, for the fun of it check this out.
Convert to hex, then to a decimal and insert into the database (x_test has one NUMBER column), then select, converting back:
SQL> insert into x_test
2 select to_number(rawtohex('Puneet_kushwah1'), rpad('X', length(rawtohex('Puneet_kushwah1')), 'X')) from dual;
1 row created.
SQL> select * from x_test;
col1
----------
4.1777E+35
SQL> SELECT utl_raw.cast_to_varchar2(hextoraw(trim(to_char(col1, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'))))
2 FROM x_test;
UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW(TRIM(TO_CHAR(col1,'XXXXXXXXXXXXXXXXXXXXXXXXXXXX
--------------------------------------------------------------------------------
Puneet_kushwah1
SQL>
While it's a horrible idea and a horrible data model, you could convert some strings into numbers by converting their raw representation into a number:
create or replace function string_to_number(p_string varchar2)
return number as
l_raw raw(40);
l_number number;
begin
l_raw := utl_i18n.string_to_raw(data => p_string, dst_charset => 'AL32UTF8');
l_number := to_number(rawtohex(l_raw), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
return l_number;
end;
/
And back again:
create or replace function number_to_string(p_number number)
return varchar2 as
l_raw raw(40);
l_string varchar2(20);
begin
l_raw := hextoraw(to_char(p_number, 'fmxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'));
l_string := utl_i18n.raw_to_char(data => l_raw, src_charset => 'AL32UTF8');
return l_string;
end;
/
Which you could use as:
insert into test (no) values (string_to_number('Puneet_kushwah1'));
1 row inserted.
select * from test;
NO
---------------------------------------
417765537084927079232028220523112497
select number_to_string(no) from test;
NUMBER_TO_STRING(NO)
--------------------------------------------------------------------------------
Puneet_kushwah1
You don't really need functions, you could do the conversions in-line; but this makes what's happening a bit clearer.
But you're restricted by the precision of the number type. I think you're limited to about 20 characters, but it'll depend a bit on the actual string and its hex representation.
(I am not endorsing this approach, it's just a mildly interesting problem).