views:

5705

answers:

6

I have a database table with these two columns:

  • Amount: numeric (18,0)
  • DecimalPlaces: numeric (18,0)

This table can store amounts in various currencies, with the decimal place removed from the amount (I can't change this data model). For example, there might be two rows like this:

  • 1290, 2 (This is £12.90, needs to appear as "12.90")
  • 3400, 0 (This is 3400 Japanese Yen, needs to appear as "3400")

I need an SQL query for both Oracle and SQL Server that will format each amount with the correct number of decimal places, preserving any trailing zeroes as illustrated above. I can't use stored procedures, a reporting tool, or Excel.

A: 

The best I've been able to come up with so far is:

select Amount/power(10, DecimalPlaces) from MyTable

But it doesn't do exactly what I want:

  • Oracle: the trailing zeroes are stripped, so US$15.00 looks like "15", not "15.00"
  • SQL Server: a whole lot of extra trailing zeroes are added, so $23.99 looks like "23.99000000000" instead of "23.99"
Andrew Swan
I've found that if I use this: convert(varchar, cast(Amount/power(10, DecimalPlaces) as money), 2)... then I get four decimal places instead of 11, which looks better.
Andrew Swan
Formatting is really better for the front end to handle.
Tom H.
Agreed, but in my case the front end doesn't know the number of decimal places for each row and therefore can't perform the necessary formatting.
Andrew Swan
+1  A: 

How about?

select 12345 amount, 2 decimalPlaces, substr( to_char( 12345 ), 1, length (to_char( 12345 ) ) - 2 ) || '.' || substr( to_char( 12345 ), -2 ) result from dual /

     amount decimalPlaces result
 ---------- ------------- ------
     12345              2 123.45
Martlark
Thanks for this idea. Unfortunately it leaves a trailing "." if decimalPlaces = 0, but this is easily fixed by replacing '.' in your expression with substr('.', 2 - decimalplaces).
Andrew Swan
A: 

Martlark's answer for Oracle led me to this solution for SQL Server:

select
  left(cast(Amount as varchar), len(cast(Amount as varchar)) - DecimalPlaces) +
  left('.', DecimalPlaces) +
  right(cast(OriginalCurrencyAmount as varchar), DecimalPlaces
) as FormattedAmount
from MyTable
Andrew Swan
+1  A: 

This is gross but worked for the current inputs on SQL server.

select 
    substring(
     CAST(
      CAST(
        (amount *  power(-0.100000000000000000,decimalPlaces*1.000000000000000000)) as numeric(36,18)
      )as varchar(30)
     )
    ,1,len(cast(amount as varchar(20))) + (CASE WHEN decimalPlaces = 0 THEN 0 ELSE 1 END )) 

from
 myTable
jms
I did not get trailing zeros in my result window.
jms
And that's the problem with this approach: a row containing (1200, 2) will appear as "12" instead of "12.00".
Andrew Swan
I see. I'll keep trying but It may help others if you add that to your example data.
jms
Thanks, I've done that.
Andrew Swan
Works for 1200,2 now.
jms
+2  A: 

Your problem is that there isn't an easy way to do this for both SQLServer and Oracle in one query.

The Correct way to do this for SQLServer is to use STR:

Select STR(Amount, 18, DecimalPlaces) from myTable;

The correct way to do this for Oracle is using to_char:

SELECT to_char (amount, '99999999999999.'||rpad('',DecimalPlaces, '0')) 
from MyTable;

The queries presented by jms and Andrew won't work in an Oracle query because Oracle SQL uses LENGTH() not LEN(). And Oracle uses to_char() not Cast().

Thomas Jones-Low
+1  A: 

In SQL server you can :

select stuff(convert(varchar,amount) ,
         len(convert(varchar,amount)) - DecimalPlaces - 1, 0, ".")
Learning