PLSQL Format number for display wtihout decimal limitation - sql

I have a problem that I am pretty sure is not unique
I have a database column that has values like the following
Col
.42
1.2
5
2.222212
Now I am appending this column to sent to front end application and need to format it. But I should not be loosing any decimal values too.
So what I would want to send to the front end application would be like
0.42
1.2
5.0
2.222212
Basically, I dont want to loose out any decimal position. But no trailing zeros should be added more than 1. Similarly, a 0 should be appended before a decimal value that starts with '.' (eg: .42) But not more than 1 leading 0 should be appended.
Breaking my head on this right now. Any help would be sincerly appreciated.
Thanks

Perhaps something like this? The keys are the 0 markers (if no digit would exist there, put in a 0) and the FM (other than what's shown as required - i.e. the units digit, the decimal point and the tenths digit, make the output as narrow as possible).
This assumes the inputs are in NUMBER datatype and the desired output is string (VARCHAR2). If the inputs are strings also, wrap them within to_number().
with
test_data ( col ) as (
select .42 from dual union all
select 1.2 from dual union all
select 5 from dual union all
select 2.222212 from dual
)
-- end of test data; the SQL query is the one line below
select col, to_char(col, 'FM99999990.0999999999') as str from test_data;
COL STR
-------- --------
0.42 0.42
1.2 1.2
5 5.0
2.222212 2.222212

Related

WHERE condition to exclude amounts with decimals ending with '0's or '5's

Objective:
I have a column 'amount' with decimals. I am trying to exclude rows where the amount value ends either with '0's or '5's.
How can I achieve that...
Column type: decimal (7,2)
Ex: numbers to exclude
10.25
11.20
100.00
You probably want this:
WHERE (CAST(your_field * 100 AS INTEGER) % 5) <> 0
But it is hard to tell without more detail on your data type. Also there can be funky rounding issues with floating point values.
An interesting way to do this uses "modular" arithmetic
where col % 0.1 not in (0.00, 0.05)
The % operator works on non-integer bases as well as integer ones.
What I did here is changed the number into a string, trimmed off the trailing blanks, and then reversed the string to take the first character to see if it was no 1 or 5
SELECT * into #test FROM (SELECT CAST(10.25 as decimal(7,2)) as val UNION SELECT 8.21 UNION SELECT 6.00) DQ
select * from #test WHERE LEFT(REVERSE(RTRIM(CAST(val as nvarchar(50)))),1) NOT IN ('5', '0')
drop table #test

Number check in oracle sql

How to check in 10 digit number whether it contain 999 or 000 in the 4-6th bytes ?
I have a n idea with using INSTR but i don't know how to execute it
This is strange. If the "number" is really a string, then you can use like or substr():
where col like '___999%' or col like '___000%'
or:
where substr(col, 4, 3) in ('999', '000')
or even regular expressions.
Given the nature of your question, you can turn a number into a string and use these methods. However, if you are looking at particular digits, then the "number" should be stored as a string.
If they are actually numbers rather than strings then you could use numeric manipulation:
with t (n) as (
select 1234567890 from dual
union all select 1239997890 from dual
union all select 1230007890 from dual
union all select 1299967890 from dual
union all select 1234000890 from dual
)
select n,
mod(n, 10000000) as stage1,
mod(n, 10000000)/10000 as stage2,
trunc(mod(n, 10000000)/10000) as stage3,
case when trunc(mod(n, 10000000)/10000) in (0, 999) then 'Yes' else 'No' end as matches
from t;
N STAGE1 STAGE2 STAGE3 MATCHES
---------- ---------- ---------- ---------- -------
1234567890 4567890 456.789 456 No
1239997890 9997890 999.789 999 Yes
1230007890 7890 .789 0 Yes
1299967890 9967890 996.789 996 No
1234000890 4000890 400.089 400 No
Stage 1 effectively strips off the first three digits. Stage two almost strips off the last four digits, but leaves fractions, so stage 3 adds trunc() (you could also use floor()) to ignore those fractional parts.
The result of that is the numeric value of the 4-6th digits, and you can then test if that is 0, 999 or something else.
This is really looking at the 4th to 6th most significant digits, which is the same if the number is always 10 digits; if it might actually have different numbers of digits then you'd need to clarify what you want to see.
select
1 from dual where instr(98800054542,000,4,3)in (6) or instr(98800054542,999,4,3)in (6); let me know if this helped.

how to add zeros after decimal in Oracle

I want to add zeroes after the number .
for eg a= 6895
then a= 6895.00
datatype of a is number(12);
I am using the below code .
select to_char(6895,'0000.00') from dual .
I m getting the desired result from above code but
'6895' can be any number.so due to that i need to add '0' in above code manually.
for eg.
select to_char(68955698,'00000000.00') from dual .
Can any one suggest me the better method .
The number format models are the place to start when converting numbers to characters. 0 prepends 0s, which means you'd have to get rid of them somehow. However, 9 means:
Returns value with the specified number of digits with a leading space if positive or with a leading minus if negative. Leading zeros are blank, except for a zero value, which returns a zero for the integer part of the fixed-point number.
So, the following gets you almost there, save for the leading space:
SQL> select to_char(987, '9999.00') from dual;
TO_CHAR(
--------
987.00
You then need to use a format model modifier, FM, which is described thusly:
FM Fill mode. Oracle uses trailing blank characters and leading zeroes
to fill format elements to a constant width. The width is equal to the
display width of the largest element for the relevant format model
...
The FM modifier suppresses the above padding in the return value of
the TO_CHAR function.
This gives you a format model of fm9999.00, which'll work:
SQL> select to_char(987, 'fm9999.00') from dual;
TO_CHAR(
--------
987.00
If you want a lot of digits before the decimal then simply add a lot of 9s.
datatype of a is number(12);
Then use 12 9s in the format model. And, keep the decimal to just 2. So, since the column datatype is NUMBER(12), you cannot have any number more than the given size.
SQL> WITH DATA AS(
2 SELECT 12 num FROM dual union ALL
3 SELECT 234 num FROM dual UNION ALL
4 SELECT 9999 num FROM dual UNION ALL
5 SELECT 123456789 num FROM dual)
6 SELECT to_char(num,'999999999999D99') FROM DATA
7 /
TO_CHAR(NUM,'999
----------------
12.00
234.00
9999.00
123456789.00
SQL>
Update Regarding leading spaces
SQL> select ltrim(to_char(549,'999999999999.00')) from dual;
LTRIM(TO_CHAR(54
----------------
549.00
SQL>
With using CASE and SUBSTR it is very simple.
CASE WHEN SUBSTR(COLUMN_NAME,1,1) = '.' THEN '0'||COLUMN_NAME ELSE COLUMN_NAME END

SQL change date formats inside a string

I would like to convert a string containing dates in SQL select from Oracle 11g database.
Original string (CLOB) example:
"1.12.2011 - event 1
2.2.2012 - event 2
13.3.2012 - event 44"
Desired output:
"20111201 - event 1
20120202 - event 2
20120313 - event 44"
Is there a better (faster) way than using 4 separate replacements?
regexp_replace(regexp_replace(regexp_replace(regexp_replace(my_string,
'(\d\d)\.(\d\d)\.(20\d\d)', '\3\2\1'),
'(\d\d)\.(\d)\.(20\d\d)', '\30\2\1'),
'(\d)\.(\d\d)\.(20\d\d)', '\3\20\1'),
'(\d)\.(\d)\.(20\d\d)', '\30\20\1')
Especially if you're using clobs you have to be careful unless you're certain of the data in there.
However, if your clob only looks like that then you need threeregexp_replace in order for this to work; it'll also be much more dynamic. Just explicitly specify digits using [[:digit:]] then specify a minimum and maximum number of times these digits could be there using {1,2}.
Then the following would work:
select regexp_replace(
regexp_replace(
regexp_replace( my_string
, '([[:digit:]]{1,2})\.([[:digit:]]{1,2})\.(20[[:digit:]]{2})'
, '\3-\2-\1')
, '-([[:digit:]]{1}(-|$))'
, '0\1' )
, ('-')
, '')
from dual
This means:
match ( group 1 ) 1 or 2 digits
match a full stop.
match ( group 2 ) 1 or 2 digits
match a full stop
match ( group 3 ) 20 + 2 digits.
Then take out only groups 1, 2 and 3, i.e. ignoring the full stops and return then in the order 3, 2, 1 padded with a hyphen
Then replace any [digit] that is followed by either a hyphen or the end of the string, i.e. the number of digits is only 1 with -0[digit].
Lastly replace all the hyphens.
Separately from that I agree with tbone. It would make a lot more sense to store this data in a separate table (event_id number, event_date date). Any string transformations are easy with no chance of getting it wrong, unlike in this situation, and the data is easy to query and compare.
there are no better options (both correct and readable) with better performance - or if there are, no one cares..
i prefer a 2-level regexp_replace for date part:
select regexp_replace(
regexp_replace( my_string,
'([[:digit:]]{1,2})\.([[:digit:]]{1,2})\.(20[[:digit:]]{2})',
'\3-0\2-0\1' ),
'(20[[:digit:]]{2})-0?([[:digit:]]{2})-0?([[:digit:]]{2})',
'\3\2\1' )
from dual;
Demo
Maybe try doing:
select to_char(to_date('13.3.2011', 'DD.MM.YYYY'),'YYYYMMDD') from dual;

Oracle - Why does the leading zero of a number disappear when converting it TO_CHAR

In Oracle, when converting a number with a leading zero to a character, why does the leading number disappear? Is this logic Oracle specific, or specific to SQL?
Example:
SELECT TO_CHAR(0.56) FROM DUAL;
/* Result = .56 */
I was looking for a way to format numbers without leading or trailing spaces, periods, zeros (except one leading zero for numbers less than 1 that should be present).
This is frustrating that such most usual formatting can't be easily achieved in Oracle.
Even Tom Kyte only suggested long complicated workaround like this:
case when trunc(x)=x
then to_char(x, 'FM999999999999999999')
else to_char(x, 'FM999999999999999.99')
end x
But I was able to find shorter solution that mentions the value only once:
rtrim(to_char(x, 'FM999999999999990.99'), '.')
This works as expected for all possible values:
select
to_char(num, 'FM99.99') wrong_leading_period,
to_char(num, 'FM90.99') wrong_trailing_period,
rtrim(to_char(num, 'FM90.99'), '.') correct
from (
select num from (select 0.25 c1, 0.1 c2, 1.2 c3, 13 c4, -70 c5 from dual)
unpivot (num for dummy in (c1, c2, c3, c4, c5))
) sampledata;
| WRONG_LEADING_PERIOD | WRONG_TRAILING_PERIOD | CORRECT |
|----------------------|-----------------------|---------|
| .25 | 0.25 | 0.25 |
| .1 | 0.1 | 0.1 |
| 1.2 | 1.2 | 1.2 |
| 13. | 13. | 13 |
| -70. | -70. | -70 |
Still looking for even shorter solution.
There is a shortening approarch with custom helper function:
create or replace function str(num in number) return varchar2
as
begin
return rtrim(to_char(num, 'FM999999999999990.99'), '.');
end;
But custom pl/sql functions have significant performace overhead that is not suitable for heavy queries.
It's the default formatting that Oracle provides. If you want leading zeros on output, you'll need to explicitly provide the format. Use:
SELECT TO_CHAR(0.56,'0.99') FROM DUAL;
or even:
SELECT TO_CHAR(.56,'0.99') FROM DUAL;
The same is true for trailing zeros:
SQL> SELECT TO_CHAR(.56,'0.990') val FROM DUAL;
VAL
------
0.560
The general form of the TO_CHAR conversion function is:
TO_CHAR(number, format)
Seems like the only way to get decimal in a pretty (for me) form requires some ridiculous code.
The only solution I got so far:
CASE WHEN xy>0 and xy<1 then '0' || to_char(xy) else to_char(xy)
xy is a decimal.
xy query result
0.8 0.8 --not sth like .80
10 10 --not sth like 10.00
That only works for numbers less than 1.
select to_char(12.34, '0D99') from dual;
-- Result: #####
This won't work.
You could do something like this but this results in leading whitespaces:
select to_char(12.34, '999990D99') from dual;
-- Result: ' 12,34'
Ultimately, you could add a TRIM to get rid of the whitespaces again but I wouldn't consider that a proper solution either...
select trim(to_char(12.34, '999990D99')) from dual;
-- Result: 12,34
Again, this will only work for numbers with 6 digits max.
Edit: I wanted to add this as a comment on DCookie's suggestion but I can't.
Try this to avoid to_char limitations:
SELECT
regexp_replace(regexp_replace(n,'^-\'||s,'-0'||s),'^\'||s,'0'||s)
FROM (SELECT -0.89 n,RTrim(1/2,5) s FROM dual);
Should work in all cases:
SELECT regexp_replace(0.1234, '^(-?)([.,])', '\10\2') FROM dual
Below format try if number is like
ex 1 suppose number like 10.1 if apply below format it will be come as 10.10
ex 2 suppose number like .02 if apply below format it will be come as 0.02
ex 3 suppose number like 0.2 if apply below format it will be come as 0.20
to_char(round(to_number(column_name)/10000000,2),'999999999990D99') as column_name
If your country supports comma for decimal, use this:
SELECT regexp_replace(
regexp_replace(to_char(YOUR_NUMBER), '^,', '0,'), -- Positives
'^-,', '-0,') -- Negatives
Otherwise, if your country supports dots for decimal, use this:
SELECT regexp_replace(
regexp_replace(to_char(YOUR_NUMBER), '^.', '0.'), -- Positives
'^-.', '-0.') -- Negatives