I have a requirement to produce amount fields in zoned decimal format with this specific syntax below.
I don’t know if I need to create a function to handle this or if I can tweak the Oracle number format model. I’m thinking it might require some conditional formatting within a function due to the different requirement for number of digits between positive and negative. I will be performing this formatting on a couple of dozen data elements in the procedure so that might be another reason to use a function. Thoughts?
Requirement:
Amount should be represented by 17 characters (positive number) or 16 characters plus a “}” appended to the end (negative number).
Ex. 0.00 should show as 00000000000000000.
Ex. -935,560.00 should show as 00000000093556000}
Using Oracle 12c.
If I understood you correctly, the input is already formatted and its datatype is VARCHAR2. If that's so, then this might do the job:
SQL> with test (col) as
2 (select '0.00' from dual union all
3 select '25.34' from dual union all
4 select '-935,560.00' from dual
5 )
6 select col,
7 lpad(translate(col, 'x,.-', 'x'),
8 case when substr(col, 1, 1) = '-' then 16
9 else 17
10 end, '0') ||
11 case when substr(col, 1, 1) = '-' then '}'
12 else null
13 end result
14 from test;
COL RESULT
----------- --------------------
0.00 00000000000000000
25.34 00000000000002534
-935,560.00 0000000093556000}
SQL>
What does it do?
lines #1 - 5 - sample data
line #7 - translate removes minus sign, commas and dots
lines #7 - 10 - lpad pads the number (without characters from the previous step) with zeros up to the length of 16 (for negative values) or 17 (for positive values) characters
lines #11 - 13 - if it is a negative value, concatenate } to the end of the result string
Related
I need to display different results in the next format, example:
40000000 to 40'000,000
I tried using this, but when i try 2 differents group separators i get the "invalid number format model" error:
select to_char(9999999999, '9g999g99999g9', 'NLS_NUMERIC_CHARACTERS='',.''')
from dual;
Also tried using substr and replace but it doesnt work in all the cases (like when the result is 3000000 or 700000000).
This works but it is not the optimal solution.:
SELECT substr(replace('40,000,000',',',''''),0,length(40000000)-2)|| substr('40,000,000',-4) from dual;
What the actual select look like if i use the previous code.
SELECT substr(replace(to_char(oTOTAL_SENIOR, '999,999,999'),',',''''),0,length(oTOTAL_SENIOR)-2)|| substr(to_char(oTOTAL_SENIOR, '999,999,999'),-4) from dual
The previous select gets bugged when i use substr replace and to_char together because of the '999,999,999'.
I also tried using regexp_replace but im not good at it.
I know i need to replace everything but the last 4 characters (,000) but i dont know how.
Any help will be aprreciated.
You'll need someone smarter than me to do that properly, but - meanwhile, see whether this helps.
I'm on XE 11g; in order to avoid "no more data to read from socket" error I got during the final steps of this query (I believe it was due to two string reversings), I created my own "reverse" function. There's undocumented reverse function, but I don't want to use it.
Basically, it reverses a string you pass as a parameter. What do I need it for? I found it simpler to reverse values, split them to 3-by-3-by-3 characters and apply those "strange" separators you want. Also, it makes the whole code simpler. It can be done without it, but - as I said - no more data to read from socket won't allow it. Sorry about that.
Now, someone will say: why didn't you (meaning: me, LF) do that using PL/SQL completely and put everything into a function? No particular reason & no problem in doing it, if necessary.
OK, here it is:
SQL> create or replace function f_reverse (par_string in varchar2)
2 return varchar2
3 is
4 retval varchar2(20);
5 begin
6 select listagg(substr(par_string, level, 1))
7 within group (order by level desc)
8 into retval
9 from dual
10 connect by level <= length(par_string);
11
12 return retval;
13 end;
14 /
Function created.
SQL> select f_reverse('1234') from dual;
F_REVERSE('1234')
---------------------------------------------------------------------
4321
SQL>
Finally, this is what you want:
SQL> with test (id, col) as
2 (select 1, 40100200 from dual union all
3 select 2, 2300400 from dual union all
4 select 3, 700500 from dual union all
5 select 4, 25700 from dual union all
6 select 5, 6300 from dual union all
7 select 6, 555 from dual
8 )
9 select id,
10 regexp_replace(
11 f_reverse(
12 substr(f_reverse(col), 1, 3) ||','||
13 substr(f_reverse(col), 4, 3) || chr(39) ||
14 substr(f_reverse(col), 7)
15 ), '^[^0-9]+', '') result
16 from test;
ID RESULT
---------- ----------
1 40'100,200
2 2'300,400
3 700,500
4 25,700
5 6,300
6 555
6 rows selected.
SQL>
What does it do?
lines #1 - 7 - sample data
lines #9 onward is useful code
lines #12 - 14 - splitting reversed sample data into substrings 3 characters in length
line #12 - concatenated , as this is thousands separator
line #13 - concatenaded chr(13) which is ', a millions separator
line #11 - reversing concatenated "reversed" string back
line #10 - removing possible non-numeric characters from the beginning of the result (those are separators for values that are shorter than "thousand" or "million")
How can I find if the fifth position is a letter and thus not a number using Oracle ?
My last try was using the following statement:
REGEXP_LIKE (table_column, '([abcdefghijklmnopqrstuvxyz])');
Perhaps you'd rather check whether 5th position contains a number (which means that it is not something else), i.e. do the opposite of what you're doing now.
Why? Because a "letter" isn't only ASCII; have a look at the 4th row in my example - it contains Croatian characters and these aren't between [a-z] (nor [A-Z]).
SQL> with test (col) as
2 (select 'abc_3def' from dual union all
3 select 'A435D887' from dual union all
4 select '!#$%&/()' from dual union all
5 select 'ASDĐŠŽĆČ' from dual
6 )
7 select col,
8 case when regexp_like(substr(col, 5, 1), '\d+') then 'number'
9 else 'not a number'
10 end result
11 from test;
COL RESULT
------------- ------------
abc_3def number
A435D887 not a number
!#$%&/() not a number
ASDĐŠŽĆČ not a number
SQL>
Anchor to the start of the string else you may get unexpected results. This works, but remove the caret (start of string anchor) and it returns 'TRUE'! Note it uses the case-insensitive flag of 'i'.
select 'TRUE'
from dual
where regexp_like('abcd4fg', '^.{4}[A-Z]', 'i');
Yet another way to do it:
regexp_like(table_column, '^....[[:alpha:]]')
Using the character class [[:alpha:]] will pick up all letters upper case, lower case, accented and etc. but will ignore numbers, punctuation and white space characters.
If what you care about is that the character is not a number, then use
not regexp_like(table_column, '^....[[:digit:]]')
or
not regexp_like(table_column, '^....\d')
Try:
REGEXP_LIKE (table_column, '^....[a-z]')
Or:
SUBSTR (table_column, 5, 1 ) BETWEEN 'a' AND 'z'
Just started working with oracle using toad ide. trying to format the numbers from a table in specific format. the numbers come in from a variable in the table and I want to display the whole numbers as whole numbers and display floats as floats. So far, I can use trim(TO_CHAR (width,'999.999')) to display all numbers with decimal points.
For example: 123.5 will be displayed as 123.500 and 100 will be displayed as 100.000.
What I want to do is display for eg: 100 as 100.
Hope this is clear and I get a solution soon.
I'm using MOD for determining decimals.
select test_value, (case when mod(test_value,1) != 0 then 'DECIMAL' else 'NODECIMAL' END) IS_DECIMAL
from (select 1.5 test_value from dual
union all
select 100 test_value from dual) test_table
If your problem is about the way Toad shows numbers, you can follow the hints in the comments.
If the problem is about the way Oracle shows numbers, converting them to strings, maybe this can help:
SQL> select to_char(1.5, 'TM9') as num from dual union all
2 select to_char(100, 'TM9') from dual;
NUM
----------------------------------------------------------------
1,5
100
You find much more in the documentation
If you need a way to check whether a number has a decimal part or not, you can simply try:
SQL> with numbers(num) as (
2 select 1.5 from dual union all
3 select 100 from dual
4 )
5 select case
6 when floor(num) = num
7 then to_char(num, 'FM999999') || ' has not a decimal part'
8 else
9 to_char(num, 'FM9999D000') || ' has a decimal part'
10 end as checkString
11 from numbers;
CHECKSTRING
------------------------------
1,500 has a decimal part
100 has not a decimal part
Three different functions return the same data type according to dump()
select
dump(utl_raw.cast_to_raw('j')),
dump(utl_raw.cast_from_number(1)),
dump(utl_raw.cast_from_binary_integer(1))
from dual;
DUMP(UTL_RAW.CAST_TO_RAW('J'))
-----------------------------------------------------
DUMP(UTL_RAW.CAST_FROM_NUMBER(1))
-----------------------------------------------------
DUMP(UTL_RAW.CAST_FROM_BINARY_INTEGER(1))
-----------------------------------------------------
Typ=23 Len=1: 106
Typ=23 Len=2: 193,2
Typ=23 Len=4: 0,0,0,1
...however non of them can be added together.
select
utl_raw.cast_to_raw('j') +
utl_raw.cast_from_number(1),
utl_raw.cast_from_number(1) +
utl_raw.cast_from_binary_integer(1),
utl_raw.cast_from_binary_integer(1) +
utl_raw.cast_to_raw('j')
from dual;
ORA-00932: inconsistent datatypes: expected NUMBER got BINARY
How can I perform arithmetic on RAW, in particular - add RAW/BINARY to RAW/BINARY?
Edit: This question arose from a need to iterate through alphabet. I spotted that it's not so obvious how to add 1 to ascii code and then return with greater letter, eg. 'a' + 1 = 'b'
You don't want to add together binary values. If you just want to iterate through the alphabet, just use the ASCII and CHR functions. For example
select ascii('a') ascii_code,
ascii('a') + 1 next_ascii_code,
chr( ascii('a') + 1 ) next_char
from dual
will show you the ASCII code of a lower case a (which will be 97 assuming your database character set is a superset of US7ASCII), the ASCII code of the next character (98), and the next character in character set (a lower case b). If you want to iterate through the alphabet in a loop
FOR i IN 1 .. 26
LOOP
dbms_output.put_line( chr( ascii('a') + i - 1 ) );
END LOOP;
I have a cursor which selects date from column with NUMBER type containg floating point numbers. Numbers like 4,3433 are returned properly while numbers smaller then 1 have removed leading zero.
For example number 0,4513 is returned as ,4513.
When I execute select used in the cursor on the database, numbers are formatted properly, with leading zeros.
This is how I loop over the recors returned by the cursor:
FOR c_data IN cursor_name(p_date) LOOP
...
END LOOP;
Any ideas why it works that way?
Thank you in advance.
You're confusing number format and number value.
The two strings 0.123 and .123, when read as a number, are mathematically equals. They represent the same number. In Oracle the true number representation is never displayed directly, we always convert a number to a character to display it, either implicitly or explicitly with a function.
You assume that a number between 0 and 1 should be represented with a leading 0, but this is not true by default, it depends on how you ask this number to be displayed. If you don't want unexpected outcome, you have to be explicit when displaying numbers/dates, for example:
to_char(your_number, '9990.99');
It's the default number formatting that Oracle provides.
If you want to specify something custom, you shall use TO_CHAR function (either in SQL query or PL/SQL code inside the loop).
Here is how it works:
SQL>
SQL> WITH aa AS (
2 select 1.3232 NUM from dual UNION ALL
3 select 1.3232 NUM from dual UNION ALL
4 select 332.323 NUM from dual UNION ALL
5 select 0.3232 NUM from dual
6 )
7 select NUM, to_char(NUM, 'FM999990D9999999') FORMATTED from aa
8 /
NUM FORMATTED
---------- ---------------
1.3232 1.3232
1.3232 1.3232
332.323 332.323
.3232 0.3232
SQL>
In this example, 'FM' - suppresses extra blanks, '0' indicates number digit including leading/trailing zeros, and '9' indicates digit suppressing leading/trailing zeros.
You can find many examples here:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34570