3

I have a table with a number column that I'm trying to format with the oracle sql function to_char. Depending on how the column is shown, the result get a trailing blank or not.

SELECT 
  '[',TO_CHAR(ABS(Balance), 'FM0000000000000000V00'),']',
  '[' || TO_CHAR(ABS(Balance),'FM0000000000000000V00') || ']'
FROM Accounts

will get me the following result :

[;000000000749460366 ;];[000000000749460366]
[;000000008751094792 ;];[000000008751094792]
[;000000000000050696 ;];[000000000000050696]
[;000000000000000000 ;];[000000000000000000]

I would be expecting the same result in both case but I cannot understand why there is a difference. Is there any reason I got these differences ?

Many thanks, Pierre-Yves

2 Answers2

4

It isn't adding a trailing space to the value - if you check the length it's still 18, and if it was then the concatenated version would have a space too. The metadata for the column is deciding the maximum possible length is 19, which seems to be true if the original balance value exceeds 16 digits:

set colsep ;
with accounts(balance) as (
  select 749460366 from dual
  union all select 1234567890123456 from dual
  union all select 12345678901234567 from dual
)
SELECT 
  '[',TO_CHAR(ABS(Balance), 'FM0000000000000000V00') as balance,']',
  '[',length(TO_CHAR(Balance, 'FM0000000000000000V00')) as length,']',
  '[' || TO_CHAR(ABS(Balance),'FM0000000000000000V00') || ']'
FROM Accounts;

';BALANCE            ;';';    LENGTH;';'['||TO_CHAR(ABS(BALA
-;-------------------;-;-;----------;-;---------------------
[;000000074946036600 ;];[;        18;];[000000074946036600] 
[;123456789012345600 ;];[;        18;];[123456789012345600] 
[;###################;];[;        19;];[###################]

A 17-digit value can't be displayed in the supplied format, and it substitutes hashes; but interestingly 19 of them, however large the balance value actually is.

The FM modifier suppresses a leading space for a positive value; but if you had a negative 16-digit value you would still need 19 digits to display it as the minus sign would be present. You know the value can't be negative because of the ABS() call, but the TO_CHAR() function doesn't know that, so it still has to allow for the minus sign in the column width.

If you're really running the query/conversion from an application, e.g. over JDBC, then the string you get back (for less than 17 digits, after the ABS()) will be 18 characters and won't have the (non-existent) trailing space.

Your client (or at least, SQL*Plus and SQL Developer) is formatting the column width based on what it thinks the widest possible value can be. You can override that:

column balance format A18

with accounts(balance) as (
  select 749460366 from dual
  union all select 1234567890123456 from dual
  union all select 12345678901234567 from dual
  union all select 123456789012345678 from dual
)
SELECT 
  '[',TO_CHAR(ABS(Balance), 'FM0000000000000000V00') as balance,']',
  '[',length(TO_CHAR(Balance, 'FM0000000000000000V00')) as length,']',
  '[' || TO_CHAR(ABS(Balance),'FM0000000000000000V00') || ']'
FROM Accounts;

';BALANCE           ;';';    LENGTH;';'['||TO_CHAR(ABS(BALA
-;------------------;-;-;----------;-;---------------------
[;000000074946036600;];[;        18;];[000000074946036600] 
[;123456789012345600;];[;        18;];[123456789012345600] 
[;##################;];[;        19;];[###################]
 ;#                 ; ; ;          ; ;                     

[;##################;];[;        19;];[###################]
 ;#                 ; ; ;          ; ;                     

... which makes the non-space disappear, but messes up even more if/when the original value exceeds 16 digits as it wraps the extra hash onto the next line.

You could also explicitly CAST the result to the length you expect:

clear columns

with accounts(balance) as (
  select 749460366 from dual
  union all select 1234567890123456 from dual
  union all select 12345678901234567 from dual
  union all select 123456789012345678 from dual
)
SELECT 
  '[',CAST(TO_CHAR(ABS(Balance), 'FM0000000000000000V00') as varchar2(18)) as balance,']',
  '[',length(TO_CHAR(Balance, 'FM0000000000000000V00')) as length,']',
  '[' || TO_CHAR(ABS(Balance),'FM0000000000000000V00') || ']'
FROM Accounts;

';BALANCE           ;';';    LENGTH;';'['||TO_CHAR(ABS(BALA
-;------------------;-;-;----------;-;---------------------
[;000000074946036600;];[;        18;];[000000074946036600] 
[;123456789012345600;];[;        18;];[123456789012345600] 
[;##################;];[;        19;];[###################]
[;##################;];[;        19;];[###################]
Alex Poole
  • 183,384
  • 11
  • 179
  • 318
0
SELECT 
  '[', lpad(ABS(Balance),18,0) ,']',
  '[' || lpad(ABS(Balance),18,0) || ']'
FROM Accounts 

this shows me same result.

FreeMan
  • 1,417
  • 14
  • 20