SQL: How to format decimals, including ones less than 1 - sql

This feels like a REALLY basic question buy I have been looking around for about an hour and have yet to find a straight answer.
I have a decimal number, it represents a weight, I want it rounded and formatted in a way that is considered normal.
1.0000 to 1.00
.1900 to 0.19
-.1900 to -0.19
Right now I'm getting rid of the trailing zeros by casting as a decimal and rounding them off
CAST(TLI_Amount as decimal(18,2))
Now heres the kicker. I don't know what version of SQL I'm on, but it must be really old. Old enough that it doesn't recognize FORMAT as a function. Which is pretty much what everyone says to use. That or the leading zero get put in front of everything, but it needs to only be there for numbers < 1
So how can I get my decimals < 1 to read as 0.xx, like any normal human readable number should be.

SQL Anywhere does support ##version so perhaps that's not what you're working on. But if you are, you can look up the documentation on the str() function here. In a nutshell:
select str( 1.2345, 4, 2 )
-> 1.23
select str( 0.1234, 4, 2 )
-> 0.12
select str( -0.1234, 5, 2 )
-> -0.12
There is also a round function.

Well, how about just adding the zero in. I think SQLAnywhere uses + for string concatenation:
SELECT (CASE WHEN TLI_Amount < 1 THEN '0' ELSE 1 END) +
CAST(CAST(TLI_Amount as decimal(18, 2)) as VARCHAR(255))
)
Note: This assumes that TLI_Amount is positive.

Related

SQL - return all float values in field without writing in scientific notation

I have a column with an extremely large set of values (>21k), with an undetermined amount of decimal places, some sample data (FLOAT) that exists:
6.46
0.784
8.05
86.4
2.64
0
2.14
1E-06
14.81
3.45
0.87243
0
1.12279
0
21.41
0.6243
I would like to return all values as shown EXCEPT "1E-06" should be .000001; further, any scientific notation should be in a purely decimal format.
I have tried to write some sort of CASE statement where the "THEN" is followed by "CONVERT(varchar(100), CAST(#testFloat AS decimal(38,x)))" where x is the amount of sig figs, but I was unable to get the right condition for the WHEN statement, here was my best try:
CASE
WHEN #testFloat = 0 THEN #testFloat
WHEN FLOOR(#testFloat*10) != #testFloat*10 THEN CONVERT(varchar(100), CAST(#testFloat AS decimal(38,2)))
WHEN FLOOR(#testFloat*100) != #testFloat*100 THEN CONVERT(varchar(100), CAST(#testFloat AS decimal(38,3)))
*etc*
END
Pretty sure I wasn't even on the right trail....
Jeroen's comment was helpful, and gave me the expression to give me the desired results:
select FORMAT([sampleField], '0.' + REPLICATE('#', 308))
Where I think I just have to bring down the '308' to a number that represents the highest level of precision seen in our processes. Thank you Jeroen for that solution!

Convert scientific notation to float in BQ

Is there an easy way to convert a number in scientific notation to float in BigQuery?
For example:
8.32E-4 to 0,08
I know this is an older question, but for anyone else looking for a solution, this query (although a little clunky) should work for all scientific notation fields.
The difficulty is that BigQuery will convert any numbers greater than E±3 to scientific notation. The workaround is to cast the number as a string. If you're using this output for reporting purposes, it shouldn't be a problem. If you need to work with the numbers, make sure you've completed all calculations before converting:
SELECT
number
,base_num
,exponent
,CASE WHEN REGEXP_EXTRACT(number, r'E([+-])') = '+'
THEN REGEXP_EXTRACT(STRING((base_num * (POW(10, exponent)))), r'(\d+\.0)')
ELSE STRING(base_num * (POW(10, (exponent * -1))))
END AS converted_number
FROM
(SELECT
number
,FLOAT(REGEXP_EXTRACT(number, r'(.*)E')) base_num
,INTEGER(REGEXP_EXTRACT(number, r'E[+-](\d+)')) exponent
FROM
(SELECT "8E+1" AS number)
,(SELECT "1.6E-3" AS number)
,(SELECT "8.32E-4" AS number)
,(SELECT "2.92E+9" AS number)
)
Explanation:
FLOAT(REGEXP_EXTRACT(number, r'(.*)E')) returns the number before the E (as a string) and then converts it to a float.
INTEGER(REGEXP_EXTRACT(number, r'E[+-](\d+)')) returns the number after the E (as a string) and then converts it to an integer.
Then, raise 10 to the exponent (or its inverse, if it's negative) and multiply this by the base number.
Finally, cast as a string to maintain non-scientific notation. For numbers greater than 1, "round" to a single 0 after the decimal using regex for easier reading.
Output:
Row number base_num exponent converted_number
1 8E+1 8.0 1 80.0
2 1.6E-3 1.6 3 0.001600
3 8.32E-4 8.32 4 0.000832
4 2.92E+9 2.92 9 2920000000.0
I supposed this is for reporting purposes maybe? Does this solve for you?
SELECT FORMAT("%.10f", 8.32E-8) number UNION ALL
SELECT FORMAT("%.6f", 8.32E-4) UNION ALL
SELECT FORMAT("%4.f", 8.32E+4) UNION ALL
SELECT FORMAT("%10.f", 8.32E+8)

Issue in rounding off the values extracted from Teradata SQL assistant

I need to round off couple of fields that I extract from Teradata SQL assistant.
Currently I am using CAST(Field1 as numeric(20,2)) as Field1
18.529 is rounded to 18.53 but 36.425 is rounded to 36.42 instead I am expecting 36.43
How can this be achieved?
The rounding rules for CASTs depend on a global setting, RoundHalfwayMagUp in dbscontrol.
You might try the ROUND function which defaults to the rounding rules you prefer:
ROUND(36.425,2)
I found an old post on a forum here which states that the RoundHalfwayMagUp controls whether .5 rounds up or down. See the docs for more info
because in 36.425, 5 is near to 0 not to 10,
if you put 36.426 it will round of to 36.43
Round :
It will work like below
between 0 and 5 > increment by 0( replaced )
between 5 to 10 > increment by 1 ( replaced)

Strange behavior while rounding in SQL server 2008

At some point I have a numeric(28,10) and I cast it in money (I know its bad but for legacy reason I have to return money) in the same time I also have to set the sign (multiplying by +1/-1).
In a first attempt I had cast the +/-1 to match the numeric type.
For the value 133.3481497944 we encounter a strange behavior (I have simplified the actual code in order to keep only the elements needed to demonstrate the problem):
SELECT CAST(CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(28,10)) AS money)
133.3482
which is not correctly rounded...
Removing the cast solve the problem
SELECT CAST(CAST(133.3481497944 AS numeric(28,10)) * 1 AS money)
133.3481
Did someone know what is happening in SQL? How can a multiplication by 1 and cast(1 AS numeric(28,10)) affect the result of the rounding?
When multiplying numerics, SQL uses the following rules to determine the precision and scale of the output:
p = p1 + p2 + 1
s = s1 + s2
which makes sense - you wouldn't want 1.5 * 2.5 to be truncated to one digit past the decimal. Nor would you want 101 * 201 to be limited to 3 digits of precision, giving you 20300 instead of 20301.
In your case that would result in a precision of 57 and a scale of 20, which isn't possible - the maximum precision and scale is 38.
If the resulting type is too big, decimal digits are sacrificed in order to preserve the integral (most significant) part of the result.
From the SQL Programmability & API Development Team Blog:
In SQL Server 2005 RTM (and previous versions), we decided preserve a minimum scale of 6 in both multiplication and division.
So your answer depands on how big and precise you need the multiplier to be. In order to preserve 10 digits of decimal precision. If the multiplier needs a scale bigger than 9, then decimal digits may be truncated. If you use a smaller precision and scale, you should be fine:
SELECT CAST(CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(9,7)) AS money)
yields 133.3481.
I don't see any ROUNDing here. I only see casting. Don't assume that it will round, when you CAST. Historically, when we cast the environment truncates (SQL server or not) or behaves not as we expect - especially when we're talking about FLOATs.
SELECT
CAST(CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(28,10)) AS money) --Your original,
CAST(1 AS numeric(28,10)) --Just the 1 casted,
CAST(133.3481497944 AS numeric(28,10)) --Your expected calculation,
CAST(133.3481497944 AS numeric(28,10))*cast(1 AS numeric(28,10)) -- The actual calculation
SELECT
CAST(133.3481497944 AS numeric(28,10))*cast(1.5 AS numeric(28,10)),
CAST(133.3481497944 AS numeric(28,10))*1.5,
CAST((133.3481497944*1) AS money),
133.3481497944*1
Returns
133.3482
1.0000000000
133.3481497944
133.348150
200.022225
200.02222469160
133.3481
133.3481497944
So as mentioned above, there really isn't any true rounding, but a loss of precision during the cast. As to exactly why, I don't know. Most likely during the calculation(multiplication) while using the Numeric(28,10) it cuts off some precision.
I added the second lines to show that really you may not need your numeric casting.

How do I count decimal places in SQL?

I have a column X which is full of floats with decimals places ranging from 0 (no decimals) to 6 (maximum). I can count on the fact that there are no floats with greater than 6 decimal places. Given that, how do I make a new column such that it tells me how many digits come after the decimal?
I have seen some threads suggesting that I use CAST to convert the float to a string, then parse the string to count the length of the string that comes after the decimal. Is this the best way to go?
You can use something like this:
declare #v sql_variant
set #v=0.1242311
select SQL_VARIANT_PROPERTY(#v, 'Scale') as Scale
This will return 7.
I tried to make the above query work with a float column but couldn't get it working as expected. It only works with a sql_variant column as you can see here: http://sqlfiddle.com/#!6/5c62c/2
So, I proceeded to find another way and building upon this answer, I got this:
SELECT value,
LEN(
CAST(
CAST(
REVERSE(
CONVERT(VARCHAR(50), value, 128)
) AS float
) AS bigint
)
) as Decimals
FROM Numbers
Here's a SQL Fiddle to test this out: http://sqlfiddle.com/#!6/23d4f/29
To account for that little quirk, here's a modified version that will handle the case when the float value has no decimal part:
SELECT value,
Decimals = CASE Charindex('.', value)
WHEN 0 THEN 0
ELSE
Len (
Cast(
Cast(
Reverse(CONVERT(VARCHAR(50), value, 128)) AS FLOAT
) AS BIGINT
)
)
END
FROM numbers
Here's the accompanying SQL Fiddle: http://sqlfiddle.com/#!6/10d54/11
This thread is also using CAST, but I found the answer interesting:
http://www.sqlservercentral.com/Forums/Topic314390-8-1.aspx
DECLARE #Places INT
SELECT TOP 1000000 #Places = FLOOR(LOG10(REVERSE(ABS(SomeNumber)+1)))+1
FROM dbo.BigTest
and in ORACLE:
SELECT FLOOR(LOG(10,REVERSE(CAST(ABS(.56544)+1 as varchar(50))))) + 1 from DUAL
A float is just representing a real number. There is no meaning to the number of decimal places of a real number. In particular the real number 3 can have six decimal places, 3.000000, it's just that all the decimal places are zero.
You may have a display conversion which is not showing the right most zero values in the decimal.
Note also that the reason there is a maximum of 6 decimal places is that the seventh is imprecise, so the display conversion will not commit to a seventh decimal place value.
Also note that floats are stored in binary, and they actually have binary places to the right of a binary point. The decimal display is an approximation of the binary rational in the float storage which is in turn an approximation of a real number.
So the point is, there really is no sense of how many decimal places a float value has. If you do the conversion to a string (say using the CAST) you could count the decimal places. That really would be the best approach for what you are trying to do.
I answered this before, but I can tell from the comments that it's a little unclear. Over time I found a better way to express this.
Consider pi as
(a) 3.141592653590
This shows pi as 11 decimal places. However this was rounded to 12 decimal places, as pi, to 14 digits is
(b) 3.1415926535897932
A computer or database stores values in binary. For a single precision float, pi would be stored as
(c) 3.141592739105224609375
This is actually rounded up to the closest value that a single precision can store, just as we rounded in (a). The next lowest number a single precision can store is
(d) 3.141592502593994140625
So, when you are trying to count the number of decimal places, you are trying to find how many decimal places, after which all remaining decimals would be zero. However, since the number may need to be rounded to store it, it does not represent the correct value.
Numbers also introduce rounding error as mathematical operations are done, including converting from decimal to binary when inputting the number, and converting from binary to decimal when displaying the value.
You cannot reliably find the number of decimal places a number in a database has, because it is approximated to round it to store in a limited amount of storage. The difference between the real value, or even the exact binary value in the database will be rounded to represent it in decimal. There could always be more decimal digits which are missing from rounding, so you don't know when the zeros would have no more non-zero digits following it.
Solution for Oracle but you got the idea. trunc() removes decimal part in Oracle.
select *
from your_table
where (your_field*1000000 - trunc(your_field*1000000)) <> 0;
The idea of the query: Will there be any decimals left after you multiply by 1 000 000.
Another way I found is
SELECT 1.110000 , LEN(PARSENAME(Cast(1.110000 as float),1)) AS Count_AFTER_DECIMAL
I've noticed that Kshitij Manvelikar's answer has a bug. If there are no decimal places, instead of returning 0, it returns the total number of characters in the number.
So improving upon it:
Case When (SomeNumber = Cast(SomeNumber As Integer)) Then 0 Else LEN(PARSENAME(Cast(SomeNumber as float),1)) End
Here's another Oracle example. As I always warn non-Oracle users before they start screaming at me and downvoting etc... the SUBSTRING and INSTRING are ANSI SQL standard functions and can be used in any SQL. The Dual table can be replaced with any other table or created. Here's the link to SQL SERVER blog whre i copied dual table code from: http://blog.sqlauthority.com/2010/07/20/sql-server-select-from-dual-dual-equivalent/
CREATE TABLE DUAL
(
DUMMY VARCHAR(1)
)
GO
INSERT INTO DUAL (DUMMY)
VALUES ('X')
GO
The length after dot or decimal place is returned by this query.
The str can be converted to_number(str) if required. You can also get the length of the string before dot-decimal place - change code to LENGTH(SUBSTR(str, 1, dot_pos))-1 and remove +1 in INSTR part:
SELECT str, LENGTH(SUBSTR(str, dot_pos)) str_length_after_dot FROM
(
SELECT '000.000789' as str
, INSTR('000.000789', '.')+1 dot_pos
FROM dual
)
/
SQL>
STR STR_LENGTH_AFTER_DOT
----------------------------------
000.000789 6
You already have answers and examples about casting etc...
This question asks of regular SQL, but I needed a solution for SQLite. SQLite has neither a log10 function, nor a reverse string function builtin, so most of the answers here don't work. My solution is similar to Art's answer, and as a matter of fact, similar to what phan describes in the question body. It works by converting the floating point value (in SQLite, a "REAL" value) to text, and then counting the caracters after a decimal point.
For a column named "Column" from a table named "Table", the following query will produce a the count of each row's decimal places:
select
length(
substr(
cast(Column as text),
instr(cast(Column as text), '.')+1
)
) as "Column-precision" from "Table";
The code will cast the column as text, then get the index of a period (.) in the text, and fetch the substring from that point on to the end of the text. Then, it calculates the length of the result.
Remember to limit 100 if you don't want it to run for the entire table!
It's not a perfect solution; for example, it considers "10.0" as having 1 decimal place, even if it's only a 0. However, this is actually what I needed, so it wasn't a concern to me.
Hopefully this is useful to someone :)
Probably doesn't work well for floats, but I used this approach as a quick and dirty way to find number of significant decimal places in a decimal type in SQL Server. Last parameter of round function if not 0 indicates to truncate rather than round.
CASE
WHEN col = round(col, 1, 1) THEN 1
WHEN col = round(col, 2, 1) THEN 2
WHEN col = round(col, 3, 1) THEN 3
...
ELSE null END