How to always keep a number with 2 decimal places? - sql

I'm trying to keep the numbers in a column in sqlite to have two decimal places after the comma. If there is only one number after the decimal point, then I must add 0. Example:
drop table if exists numbers;
create table numbers (number float);
insert into numbers values ('97.516,82');
insert into numbers values ('97.409,9');
insert into numbers values ('97.409,87');
insert into numbers values ('97.331,6'); -- correct 97.331,60
insert into numbers values ('97.229,00');
insert into numbers values ('97.183,70');
insert into numbers values ('97.163,25');
insert into numbers values ('97.163,1'); -- correct 97.163,10
insert into numbers values ('97.121,98');
insert into numbers values ('97.105,56');
insert into numbers values ('97.101,44');
insert into numbers values ('97.076,77');
insert into numbers values ('97.060,28');
insert into numbers values ('97.019,17');
insert into numbers values ('97,34');
insert into numbers values ('97,3'); -- correct 97,30
insert into numbers values ('969,19');
insert into numbers values ('968,89');
insert into numbers values ('968,7'); -- correct 968,70
insert into numbers values ('968,76');
select * from numbers
I commented the way it should be formatted, I already tried the following
select printf('%.2f',number) from numbers
But it stays in this format that I don't want
Can anyone help?

printf('%.2f',number) is the correct way to always display two decimal places, but the problem is in the INSERT not in the query.
To get the correct result in the query you need to correct the values you insert.
Sqlite has Flexible Data Typing. Since you declared the column as FLOAT, it will try to convert any value you try to insert to an INTEGER or REAL (in order of preference).
Since in every value a , character is present, Sqlite will interpret it as a string and store it as a TEXT value.
When you try to convert to a floating point with printf('%.2f',number), the point is interpreted as decimal separator and anything after a comma , is discarded, so the first value you insert is converted to 97.516 (which is then rounded by printf('%.2f',number) to 97.52) while the last is converted to 968 (and printed as 968.00).
printf('%.2f',replace(replace(number,'.',''),',','.') is a good way to do this, but you can't add thousands separators. To add them you need to concatenate the integer part and the decimal part as if they where two integers:
select printf('%,d.%02d',replace(replace(number,'.',''),',','.'),replace(replace(number,'.',''),',','.')*100%100) from numbers
The first value will be converted to an integer with thousand separators, then a point and then the second number (the first two digits of the decimal parts treated as an integer).

Step 1: Identify the digit at the thousandths place or the third place to the right of the decimal point.
Step 2: If the thousandths place digit is greater than or equal to 5, the hundredths place digit will be increased by one. And if the thousandths place digit is less than 5, the hundredths place digit will remain unchanged.
Step 3: Ignore all the remaining digits in the decimal part of the number.
Look at the image given below showing the rounding up value of a decimal number 2.835706 up to two decimal places.
round to two decimal places
Let us take one more example of round to two decimal places to understand this concept better. Round 5.803 to two decimal places.
To do this, let us follow the above steps. The first step is to identify the thousandths place digit which is 3 in this number. Now, since 3<5, we have to keep the digit at the hundredths place unchanged. Therefore, 5.803 ≈ 5.80 (where ≈ is the symbol for approximation).
Use Cuemath's free online round to two decimal places calculator to verify your answers.

Related

SQL Decimal formatting not working properly in all cases

SQL Server decimal function not working as intended.
To test with sample data, I created a table and inserted values to it.
Then, I tried to run decimal function on these values.
CREATE TABLE TEST_VAL
(
VAL float
)
SELECT * FROM TEST_VAL
Output:
VAL
----------
16704.405
20382.135
2683.135
SELECT CAST(VAL AS DECIMAL(15, 2)) AS NEWVAL
FROM TEST_VAL;
Output:
NEWVAL
-------------
16704.40
20382.13
2683.14
I expected same formatting for all 3 values. But, for third value it returns ceiling round off value.
This is due to the nature of floating point numbers being inexact and being in binary. But I want to demonstrate how this is working.
The issue is that a decimal such as 0.135 cannot be represented exactly. As the floating point representation, it would typically be something like:
0.134999999234243423
(Note that these numbers as with all representations of values in this answer are made up. They are intended to be representative to make the point.)
The number of 9s is actually larger. And the subsequent digits are just representative. In this representation, we wouldn't see a problem with truncating the value. After all 0.1349999 should round to the same value as 0.13499.
In binary, this looks different:
0.11101000010101 10011 10011 10011 10011 . . .
---------------- --------------
~0.135 "arbitrary" repeating pattern
(Note: The values are made up!)
That is, the "infinite" portion of binary representation is not a bunch of repeating 1s or repeating 0s; it has a pattern. This is analogous the inverse of most numbers in base 10 For instance, 1/7 has a repeating component of six digits, 142857. We tend to forget this, because common inverses are either exact (1/5 = 0.2) or have a single repeating digit (1/6 = 0.166666...). 1/7 is the first case that is not so simple -- and almost all decimals are like this. For rational numbers, there is always a repeating sequence regardless of base and it is never longer than dividend (number at the bottom) minus 1).
We can think of this as all decimal representations (regardless of base) always have some number of digits that are repeating. For an exact representation, the repeating portion is 0. For others it is rarely one digit. Usually, it is multiple digits. And it is a fun exercise in mathematics to characterize this. But all that is important is that the repeating portion has 1s and 0s.
Now, what is happening. A floating point number has three parts:
a magnitude. This is a number of bits that represent the exponent.
an integer portion, which is the number before the decimal point.
an integer portion, which is the number after the decimal point.
(Actually, the last two are really one integer, but I find it much easier to explain this by splitting them into two components.)
Only a fixed number of bits are available for the two integer portions. What does this look like? Once again the representative patterns are something like this:
0.135 0 11101000010101100111001110
1.135 1 11101000010101100111001110
2.135 10 1110100001010110011100111
4.135 100 111010000101011001110011
8.135 1000 11101000010101100111001
16.136 10000 1110100001010110011100
-----------^ part before the decimal
------------------^ part after the decimal
Note: This is leaving off the magnitude portion of the decimal representation.
As you can see, digits get chopped off from the end. But sometimes it is 0 that gets chopped off -- so there is no change in the value being represented. And sometimes it is a 1. And there is a change.
With this, you might be able to see how the values essentially fluctuate, say:
0.135 --> 0.135000000004
1.135 --> 0.135000000004
2.135 --> 0.135000000004
4.135 --> 0.135000000001
8.135 --> 0.135999999997
16.135 --> 0.135999999994
These are then rounded differently, which is what you are seeing.
I put together this little db<>fiddle, so you can see how the rounding changes around powers of two.
Perhaps this could be explained if we extend the precision of the three numbers in the first query:
16704.4050
20382.1349
2683.1351
Rounding each of the above to only two decimal places, which is what a cast to DECIMAL(10,2) would do, would yield:
16704.40
20382.13
2683.14
Would this be of use:
select CONVERT(DECIMAL(15,2), ROUND(VAL, 2, 1)) AS NEWVAL
from TEST_VAL;
Here is the DEMO for SQLServer 2012 : DEMO
first question : why they are not same value?
because their type is different , CAST(VAL as decimal(4,2)) will format like ##.## not ##.### so in your case it get ceiling round value.
Why not use the same type ?
CREATE TABLE T
(
[VAL] DECIMAL(8,3)
);
INSERT INTO T ([VAL])
VALUES (16704.405), (20382.135), (2683.135);
SELECT * FROM T
Output:
VAL
-----------
16704.405
20382.135
2683.135
db<>fiddle here
or you can cast AS DECIMAL(8, 3)
SELECT CAST(VAL AS DECIMAL(8,3)) AS NEWVAL
FROM T;

Convert number to decimal in pentaho

In pentaho,i am facing convertion problem when insert the data from selected values. I need to be able to pick up whatever is in that field "as is" and not change it at all.
example
Field - 0.13
inserted field -0
0.13 is converted to 0 but it should be 0.13 only. where as 110 is converted to 110 correctly. Issue with the decimal values, all decimal values converted to 0.
Thanks
There are a few things you can check on the Select Values.
Decimal Field - This checks which is the sign for decimal value for this number, it uses your system Decimal value, so if you are using English Windows/Unix, the default decimal is the dot, where as in other regions it might be the comma.
Always check which, dot or comma, you are receiving for the number before converting.
One quick note as well, Steps AFTER a Group By will receive ANY number with the mask #.#, which is only 1 decimal number after the sign. The data is not lost, it's simply shown with a different mask, be sure to also put that in the select values as well.
The Select Values should look like this for a Number like 0.13 to show as such
EDIT:
Note that in Precision and Format i have used the same number of zeros after the decimal sign, this will account for a maximum of 5 decimal cases after the sign, as a mask, if you have values with more than 5 decimal cases it will load as such, just not show.
In "foreign" Countries ( with e.g. SQLite ) these two steps might help:
1st: in the select statement of "Table input" replace the field (e.g. TRANSAMOUNT ) with field*1.0 field (e.g. TRANSAMOUNT *1.0 TRANSAMOUNT ), so every value will be casted implicitly and
2nd: in the Meta-data Tab ( as mentioned below ) change the Type to Number and choose the appropriate "Date Locate" ( e.g. de_DE ) which also affects Numbers ..

I have created CHECK constraint but receiving error message

create sequence student_studentid_seq
increment by 10
start with 100
nocycle;
create table student
(studentid number(10),
name varchar2(30) not null,
ss# number(9) unique,
gpa number(2,3) not null,
constraint student_studentid_pk PRIMARY KEY (studentid),
constraint student_gpa_ck CHECK (GPA >= 0) );
insert into student (studentid, name, ss#, gpa)
values(student_studentid_seq.NEXTVAL,'Draze Katan', 323456789,1);
receiving error message:
Error starting at line 29 in command:
insert into student (studentid, name, ss#, gpa)
values(student_studentid_seq.NEXTVAL,'Draze Katan', 323456789,1)
Error report:
SQL Error: ORA-01438: value larger than specified precision allowed for this column
01438. 00000 - "value larger than specified precision allowed for this column"
*Cause: When inserting or updating records, a numeric value was entered
that exceeded the precision defined for the column.
*Action: Enter a value that complies with the numeric column's precision,
or use the MODIFY option with the ALTER TABLE command to expand
the precision.
So it appears error message is for next constraint:constraint student_gpa_ck CHECK (GPA >= 0) );
In insert statement if I enter '0' for GPA raw will be inserted but anything more I will receive error message.
This is one of my exercise questions, I can't figure out. I just need hint where mistake is not full resolution. Please if you could help me out.
The issue is in the way you create the table, in particular in the column GPA.
You are using number(2, 3), which looks like "build a number with 2 total digits and 3 decimal digits".
In oracle documentation you find a better explanation about the NUMBER data type, its attributes and what things like number(2,3) mean:
Specify a fixed-point number using the following form:
NUMBER(p,s) where:
p is the precision, or the maximum number of significant decimal
digits, where the most significant digit is the left-most nonzero
digit, and the least significant digit is the right-most known digit.
Oracle guarantees the portability of numbers with precision of up to
20 base-100 digits, which is equivalent to 39 or 40 decimal digits
depending on the position of the decimal point.
s is the scale, or the number of digits from the decimal point to the
least significant digit. The scale can range from -84 to 127.
Positive scale is the number of significant digits to the right of the
decimal point to and including the least significant digit.
Negative scale is the number of significant digits to the left of the
decimal point, to but not including the least significant digit. For
negative scale the least significant digit is on the left side of the
decimal point, because the actual data is rounded to the specified
number of places to the left of the decimal point. For example, a
specification of (10,-2) means to round to hundreds.
Scale can be greater than precision, most commonly when e notation is
used. When scale is greater than precision, the precision specifies
the maximum number of significant digits to the right of the decimal
point. For example, a column defined as NUMBER(4,5) requires a zero
for the first digit after the decimal point and rounds all values past
the fifth digit after the decimal point.
For example:
SQL> create table tabError( a number (2, 3));
Table created.
SQL> insert into tabError values (1);
insert into tabError values (1)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL> insert into tabError values (0.1);
insert into tabError values (0.1)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL> insert into tabError values (0.01);
1 row created.
If you need 2 digits for the integer part and 3 for decimals, you need number(5, 3) or, according to Mathguy's comment, if you need numbers with one integer digit and 2 decimals, you need number(3,2).

SQL server 'like' against a float field produces inconsistent results

I am using LIKE to return matching numeric results against a float field. It seems that once there are more than 4 digits to the left of the decimal, values that match my search item on the right side of the decimal are not returned. Here's an example illustrating the situation:
CREATE TABLE number_like_test (
num [FLOAT] NULL
)
INSERT INTO number_like_test (num) VALUES (1234.56)
INSERT INTO number_like_test (num) VALUES (3457.68)
INSERT INTO number_like_test (num) VALUES (13457.68)
INSERT INTO number_like_test (num) VALUES (1234.76)
INSERT INTO number_like_test (num) VALUES (23456.78)
SELECT num FROM number_like_test
WHERE num LIKE '%68%'
That query does not return the record with the value of 12357.68, but it does return the record with the value of 3457.68. Also running the query with 78 instead of 68 does not return the 23456.78 record, but using 76 returns the 1234.76 record.
So to get to the question: why having a larger number causes these results to change? How can I change my query to get the expected results?
The like operator requires a string as a left-hand value. According to the documentation, a conversion from float to varchar can use several styles:
Value Output
0 (default) A maximum of 6 digits. Use in scientific notation, when appropriate.
1 Always 8 digits. Always use in scientific notation.
2 Always 16 digits. Always use in scientific notation.
The default style will work fine for the six digits in 3457.68, but not for the seven digits in 13457.68. To use 16 digits instead of 6, you could use convert and specify style 2. Style 2 represents a number like 3.457680000000000e+003. But that wouldn't work for the first two digits, and you get an unexpected +003 exponent for free.
The best approach is probably a conversion from float to decimal. That conversion allows you to specify the scale and precision. Using scale 20 and precision 10, the float is represented as 3457.6800000000:
where convert(decimal(20,10), num) like '%68%'
When you are comparing number with LIKE it is implicitly converted to string and then matched
The problem here is that float number is not precise and when it is converted you can get
13457.679999999999999 instead of 13457.68
So to avid this explicitly format number in appropriate format(not sure how to do this in sql server, but it will be something like)
SELECT num FROM number_like_test
WHERE Format("0.##",num) LIKE '%68%'
The conversion to string is rounding your values. Both CONVERT and CAST have the same behavior.
SELECT cast(num as nvarchar(50)) as s
FROM number_like_test
Or
SELECT convert(nvarchar(50), num) as s
FROM number_like_test
provide the results:
1234.56
3457.68
13457.7
1234.76
23456.8
You'll have to use the STR function and correct format parameters to try to get your results. For example,
SELECT STR(num, 10, 2) as s
FROM number_like_test
gives:
1234.56
3457.68
13457.68
1234.76
23456.78
Pretty well solved already, but you only need to CAST once, not twice like the other answer suggests, LIKE takes care of the string conversion:
SELECT *
FROM number_like_test
WHERE CAST(num AS DECIMAL(12,6)) LIKE '%68%'
And here's a SQL Fiddle showing the rounding behavior: SQL Fiddle
It's probably because a FLOAT data type represents a floating point number which is an approximation of the number and should not be relied on for exact comparisons.
If you need to do a search that includes the float value you would need to either store it in a decimal data type (which will hold the exact number) or convert it to a varchar using something like the STR() function

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