How to convert char to number with mask - oracle

I can't convert char symbols with dot to number. I get exception
ORA-01481 invalid format mask
Which mask would work for me? I need unlimited number of characters before the dot.
The star or n symbol doesn't work
select to_number('840.11', '*.99') from dual

The behaviour of to_number depends on database or even session settings, so in order to be sure to convert it the way you need, you need to supply the numeric characters, like this:
select to_number('840.11', '999.99', ' NLS_NUMERIC_CHARACTERS = ''. ''') from dual
Now if you have a higher number of digits before the dot, you can simply enlarge the format mask, like this:
select to_number('234234840.11', '99999999999999.99', ' NLS_NUMERIC_CHARACTERS = ''. ''')
from dual
There is no direct way to specify a dynamic amount of digits before the dot, but there is some kind of workaround described here:
Dynamic length on number format in to_number Oracle SQL

You can build a fitting formatmask using TRANSLATE and then ROUND (or TRUNC) the solution of the transformation to your needed accuracy:
SELECT ROUND(TO_NUMBER('840.1155',TRANSLATE('840.1155', '123456789', '000000000')),2) FROM dual
=> 840.12

Related

How to retrieve currency amount as words using oracle sql

In the below code, I am trying to get the amount in words for the value in the AMOUNT column but can't seem to get it right. Anyone with an idea? Below the CREATE VIEW statement:
SELECT GLR3 AS RECEIPT_DOC_NO,
GLANI AS ACCOUNT_NUMBER,
GLSBL AS JDE_NO,
(SELECT YAALPH FROM PRODDTA.F060116 WHERE YAAN8 = T1.GLSBL) AS STAFF_NAME,
CASE GLDGJ WHEN 0 THEN TO_DATE (TO_CHAR (1 + 1900000), 'YYYYDDD')
ELSE TO_DATE (TO_CHAR (GLDGJ + 1900000), 'YYYYDDD') END AS GL_DATE,
GLEXA AS NARRATIVE,
GLLT AS LEDGER_TYPE,
GLSBLT AS SUBLEDGER_TYPE,
GLCRCD AS CURRENCY_CODE,
CASE GLAA WHEN 100 THEN ROUND (GLAA / 100, 2) ELSE ROUND (GLAA / 100, 2)
END AS AMOUNT,
(SELECT TO_CHAR(TO_DATE(T1.GLAA,'J'),'JSP')) FROM DUAL) AS AMOUNT_INWORDS
FROM PRODDTA.F0911 T1;
My code is failing with
ORA-00923: FROM keyword not found where expected.
ORA-00923: FROM keyword not found where expected.
Indicates a syntax error. In this case you have three ( and four ):
(SELECT TO_CHAR(TO_DATE(T1.GLAA,'J'),'JSP')) FROM DUAL) AS AMOUNT_INWORDS
The compiler is not expecting the second ) after 'JSP'.
The scalar cursor is unnecessary, so the simplified and corrected version would be:
TO_CHAR(TO_DATE(T1.GLAA,'J'),'JSP') AS AMOUNT_INWORDS
ORA-01854: julian date must be between 1 and 5373484
5373484 is the Julian date for 31-DEC-9999 which is the maximum date allowed in Oracle. This poses an absolute limit on the number which we can spell using JSP mask ... in one go. However, we can use PL/SQL to break larger numbers into chunks and spell those instead. The inestimable Tom Kyte wrote such a function and published it on the AskTom site. Find it here.
I think the value i'm trying to write in words has decimal values (400.00) hence the ORA-01854 error.
The Tom Kyte I linked to does address that issue further down the page. It's this comment here.
Data retrieved in amount column is preceded by a negative symbol when it shouldn't be,
If you simply want to ignore negative values then apply the ABS() function to give you the absolute value.

How to avoid to_char() cast in concatenation oracle?

How to avoid to_char() cast with formatting fractional value to avoid possible errors in future depending on input values? e.g.
SELECT -0.56 || 'value' FROM DUAL;
will return
-.56value
and I would like to have
-0.56value.
I know that I can use
select to_char(-0.56, '990.00') || 'value' from dual;
But I don't know how many digits can be in the future
You can use to_char(-0.56, '999999990.00') with enough 9's to cover all your possible values. (There is always a limit, since the NUMBER data type is at most 38 digits - and yes, you could have 37 9's and a 0 in the format model.)
This may add some unwanted spaces to the left; to modify that behavior, use the fm modifier: to_char(-0.56, 'fm99999999990.00').
try this
select rtrim(to_char(-0.56, 'FM9999999990.999999999'), '.') || 'value' from dual;

Oracle 01481. 00000 - "invalid number format model"

This is my query i have used to get the value as money. Nut when concat value getting above exception. The query is
select to_char(b.balance,'9999.'||d.number_of_decimal_places) from balance b, decimal d
Am stuck with this problem.
If you have numeric number_of_decimal_places values like 1, 2, 3 etc. then you are constructing a format model like, for example, '9999.2' instead of '9999.99'.
You can convert that integer value to the format model with rpad or lpad:
select to_char(b.balance,'9999.'||rpad('9', d.number_of_decimal_places, '9'))
from balance b, decimal d
Or for trailing zeros:
select to_char(b.balance,'9999.'||rpad('0', d.number_of_decimal_places, '0'))
from balance b, decimal d
If you have string number_of_decimal_places values like '9', '99', '999' etc. then the concatenation you have will work unless you have an invalid value in one of the rows, which would be any character other than a 9 or a 0.
That includes spaces, which you could have in a varchar2 or char field. Either way you could remove those with trim:
select to_char(b.balance,'9999.'||trim(d.number_of_decimal_places))
from balance b, decimal d
But if you have any other characters then you will need to identify and correct the data in those rows; and even with spaces it would be better to fix the data if it's a varchar2 column.
It would be better to use new-style joins; I haven't changed these examples because it isn't clear if you are doing a cartesian product or have just omitted the join conditions.
If number_of_decimals returns a value like 2 then:
SELECT TO_CHAR( b.balance, RPAD( '9999.', 5 + d.number_of_decimals, '9' ) )
FROM balance b
CROSS JOIN
decimal d
For whatever reason, the concatenation of '9999.'||d.number_of_decimal_places is generating an invalid mask. We can only guess at the actual table values, presence of spaces, or whatever else may be causing issues with what it is doing.
So your solution is to run:
select '9999.'||d.number_of_decimal_places from decimal d
See what the actual format mask is that you are generating, and adjust as necessary.

Decimal place require in Oracle

case 1: SELECT TO_CHAR(12345.6789, '99999D99') FROM dual;
Output: 12345.67
case 2: SELECT TO_CHAR(12345.6789, '999D99') FROM dual;
Output: ######
case 3: SELECT TO_CHAR(12345, '99999D99') FROM dual;
Output: 12345.00
case 4: SELECT TO_CHAR(12345.1, '99999D99') FROM dual;
Output: 12345.10
Here Problem is if we don't know how many digits before decimal then how to manage for correct answer.[only case 1,3,4 can resolved using by TO_CHAR but how to solve for case 2.]
In this case the simplest answer might be to not supply a format model at all, but truncate or round the value to two decimal places:
SELECT TO_CHAR(ROUND(12345.6789, 2)) as rounded,
TO_CHAR(TRUNC(12345.6789, 2)) as truncated
FROM dual;
ROUNDED TRUNCATED
-------- ---------
12345.68 12345.67
From the documentation:
If you omit fmt, then n is converted to a VARCHAR2 value exactly long enough to hold its significant digits.
Otherwise you'd need to supply a format model that allowed for the maximum size of your number; if it's unrestricted you'd need 36 nines, the decimal separator, and two more nines. The result would be padded with spaces so you might also want to trim it, depending on how you'll use the string value.
SELECT TO_CHAR(12345.6789, '999999999999999999999999999999999999D99') as val
FROM dual;
VAL
----------------------------------------
12345.68
You could also do that flexibly by using the length of the truncated value (i.e. once the decimal places have been removed):
SELECT TO_CHAR(12345.6789,
lpad('9', length(trunc(12345.6789)), '9') || 'D99') as val
FROM dual;
VAL
---------
12345.68
But that seems unnecessarily complicated when you can let Oracle work it out for you.
However, if you want the decimals to show trailing zeros then you might need to use that method; but with zeros after the decimal separator:
SELECT TO_CHAR(12345.6, lpad('0', length(trunc(12345.6)), '9') || 'D00') as val
FROM dual;
VAL
---------
12345.60
... which addresses the 3rd and 4th cases you added. I've made it show a leading zero for values less than 1 as well; the generated format model in this case is '99990D00'. The number of nines will still vary depending on the size of your number.
By default Oracle still leaves a space at the start for a potential minus sign. You can avoid that with the FM format modifier:
SELECT TO_CHAR(12345.6, 'FM'
|| lpad('0', length(trunc(12345.6)), '9') || 'D00') as val
FROM dual;
VAL
--------
12345.60
You could always go with the maximum number of digits you expect to be present in the input. If there are fewer digits in the input than your format specifier, it wouldn't affect the outcome in anyway. For instance,
select to_char(12323.5553,'99999D99') from dual
would produce,
123.56
As you said, the length of the input is unknwon. So why would you use a fixed length formater for somthing that is unknown? Does not work. Read your input as String from the beginning and manipulate it as String or even better - BLOB.
Well, to do the rounding correct might be tricky.
So, best check your data if the numbers will realy get so big, because that would mean a lot of work and trouble.
If not more than 38 Digits are needed, you can go with decimal or numeric datatype and (if you insist on a formatter) use the TM formatter for example.
SELECT to_char(cast(1234.456 as decimal( *,2)), 'TM') as a FROM dual
or take the advice that was given above by the other posters.

Why is Oracle's to_char() function adding spaces?

Why is Oracle's to_char() function adding spaces?
select length('012'),
length(to_char('012')),
length(to_char('12', '000'))
from dual;
3, 3, 4
The extra leading space is for the potential minus sign. To remove the space you can use FM in the format:
SQL> select to_char(12,'FM000') from dual;
TO_C
----
012
By the way, note that to_char takes a NUMBER argument; to_char('012') is implicitly converted to to_char(to_number('012')) = to_char(12)
To make the answers given more clear:
select '['||to_char(12, '000')||']',
'['||to_char(-12, '000')||']',
'['||to_char(12,'FM000')||']'
from dual
[ 012] [-012] [012]
The format mask that you are using is fixed width and allows for a minus sign
Be aware when using the 'fm' syntax it will not include any values after the decimal place unless specified using zeros. For example:
SELECT TO_CHAR(12345, 'fm99,999.00') FROM dual
returns: '12,345.00'
SELECT TO_CHAR(12345, 'fm99,999.99') FROM dual
returns: '12,345.'
As you can see this would be an issue if you are expecting two zeros after the decimals place (maybe in fee reports for example).

Resources