confuse SQL Server datatype decimal over 15 digit after comma - sql-server

I have table with structure as follow :
Table1 (
id int NULL,
description varchar(50) null,
rate decimal(18,15) NULL
)
and when I test insert data into the table problem value like this :
insert into Table1 (id, description, rate)
values (1, 'My Room Upstair', 38397.0893181818)
and the error like this :
Msg 8115, Level 16, State 8, Line 1 Arithmetic overflow error
converting numeric to data type numeric.
But when I use Float data type the data inserted successful. But the rate value change to different value.
need advice please...
solution of decimal data type problem

The problem is that you are not using the decimal data type correctly.
The data type is defined like this decimal(a, b).
a is used to specify the amount of digits in the value. The default value is 18, but you can choose whatever number between 1, which is the minimum and 38, which is the maximum.
b is used for the amount of digits after the decimal point. The default value is 0 but you can specify choosing between the minimum and maximum, which are the same 1 and 38. b can only be specified if a is also specified. It needs to be equal or less than a.
In your example decimal(18,15), you can insert a number that is 18 digits long, 3 before and 15 after the decimal point. 38397.0893181818 has 5 digits before the decimal point, which is why you get the error. So if you need to add 38397.0893181818, you need decimal(15,10), since the number has 15 digits, 10 of which are after the decimal point.

The problem is that the decimal(a,b) parameters work according to what you need, a defines the number of total digits and b defines the number of digits of that total that will go after the decimal point.
https://learn.microsoft.com/es-es/sql/t-sql/data-types/decimal-and-numeric-transact-sql?view=sql-server-ver16

Related

How to convert VARCHAR columns to DECIMAL without rounding in SQL Server?

In my SQL class, I'm working with a table that is all VARCHAR. I'm trying to convert each column to a more correct data type.
For example. I have a column called Item_Cost that has a value like:
1.25000000000000000000
I tried to run this query:
ALTER TABLE <table>
ALTER COLUMN Item_Cost DECIMAL
This query does run successfully, but it turns it into 1 instead of 1.25.
How do I prevent the rounding?
Check out the documentation for the data type decimal. The type is defined by optional parameters p (precision) and s (scale). The latter determines the numbers to the right of the decimal point.
Extract from the documentation (I highlighted the important bit in bold):
s (scale)
The number of decimal digits that are stored to the right of
the decimal point. This number is subtracted from p to determine the
maximum number of digits to the left of the decimal point. Scale must
be a value from 0 through p, and can only be specified if precision is
specified. The default scale is 0 and so 0 <= s <= p. Maximum storage
sizes vary, based on the precision.
Defining a suitable precision and scale fixes your issue.
Sample data
create table MyData
(
Item_Cost nvarchar(100)
);
insert into MyData (Item_Cost) values ('1.25000000000000000000');
Solution
ALTER TABLE MyData Alter Column Item_Cost DECIMAL(10, 3);
Result
Item_Cost
---------
1.250
Fiddle

Way to show items where more than 5 decimal places occur?

I am trying to filter out some query results to where it only shows items with 6 decimal places. I don't need it to round up or add 0's to the answer, just filter out anything that is 5 decimal places or below. My current query looks like this: (ex. if item is 199.54215 i dont want to see it but if it is 145.253146 i need it returned)
select
TRA_CODPLANTA,
TRA_WO,
TRA_IMASTER,
tra_codtipotransaccion,
tra_Correlativo,
TRA_INGRESOFECHA,
abs(tra_cantidadparcial) as QTY
from mw_tra_transaccion
where FLOOR (Tra_cantidadparcial*100000) !=tra_cantidadparcial*100000
and substring(tra_imaster,1,2) not in ('CP','SG','PI','MR')
and TRA_CODPLANTA not in ('4Q' , '5C' , '5V' , '8H' , '7W' , 'BD', 'DP')
AND tra_INGRESOFECHA > #from_date
and abs(tra_cantidadparcial) > 0.00000
Any assistance would be greatly appreciated!
Here is an example with ROUND, which seems to be the ideal function to use, since it remains in the realms of numbers. If you have at most 5 decimal places, then rounding to 5 decimal places will leave the value unchanged.
create table #test (Tra_cantidadparcial decimal(20,10));
INSERT #test (Tra_cantidadparcial) VALUES (1),(99999.999999), (1.000001), (45.000001), (45.00001);
SELECT * FROM #test WHERE ROUND(Tra_cantidadparcial,5) != Tra_cantidadparcial;
drop table #test
If your database values are VARCHAR and exist in the DB like so:
100.123456
100.1
100.100
You can achieve this using a wildcard LIKE statement example
WHERE YOUR_COLUMN_NAME LIKE '%.[0-9][0-9][0-9][0-9][0-9][0-9]%'
This will being anything containing a decimal place followed by AT LEAST 6 numeric values
Here is an example using a conversion to varchar and using the LEN - the CHARINDEX of the decimal point, I'm not saying this is the best way, but you did ask for an example in syntax, so here you go:
--Temp Decimal value holding up to 10 decimal places and 10 whole number places
DECLARE #temp DECIMAL(20, 10) = 123.4565432135
--LEN returns an integer number of characters in the converted varchar
--CHARINDEX returns the integer location of the decimal where it is found in the varchar
--IF the number of characters left after subtracting the index of the decimal from the length of the varchar is greater than 5,
--you have more than 6 decimal places
IF LEN(CAST(#temp AS varchar(20))) - CHARINDEX('.', CAST(#temp AS varchar(20)), 0) > 5
SELECT 1
ELSE
SELECT 0
Here is a shorthand way.
WHERE (LEN(CONVERT(DOUBLE PRECISION, FieldName % 1)) - 2) >=5
One way would be to convert / cast that column to a lower precision. Doing this would cause automatic rounding, but that would show you if it is 6 decimals or not based on the last digit. If the last digit of the converted value is 0, then it's false, otherwise it's true.
declare #table table (v decimal(11,10))
insert into #table
values
(1.123456789),
(1.123456),
(1.123),
(1.123405678)
select
v
,cast(v as decimal(11,5)) --here, we are changing the value to have a precision of 5. Notice the rounding.
,right(cast(v as decimal(11,5)),1) --this is taking the last digit. If it's 0, we don't want it
from #table
Thus, your where clause would simply be.
where right(cast(tra_cantidadparcial as decimal(11,5)),1) > 0

What is the max value of numeric(19, 0) and what happens if it is reached?

In an SQL Server table, I have a column with the following type:
numeric(19, 0)
What is the max value for this type?
This corresponds to a Long in Java, which has a maximum value of 9,223,372,036,854,775,807 (19 digits).
Does the above type in SQL Server have the same max value?
What happens in SQL Server if the above value is reached?
The MSDN documentation rather unhelpfully says:
When maximum precision is used, valid values are from - 10^38 +1 through 10^38 - 1...Maximum storage sizes vary, based on the precision.
(max precision is numeric(38)).
From experimenting in SQL Server 2012, the maximum value for numeric(19,0) is 10e18 - 1025. Attempting to insert any higher value gives:
Msg 8115, Level 16, State 6, Line xx Arithmetic overflow error converting float to data type numeric.
The maximum does vary by precision and appears to be a bit under 10^(p-1) (where p is precision), though the exact relationship is not obvious. No doubt a more exact answer could be given by delving into the bit-level details of the storage method.
numeric(19,0) specifies a fixed precision number having a precision of 19 digits and 0 digits to the right of the decimal point. It should not overflow. See decimal and numeric for more information.
First, decimal is synonimous with numeric.
decimal (2, 1) means that total number of all digits summed before and after dot can be maximum 2 and the number of digits after dot can be maximum 1. So range is from -9.9 to 9.9
Range for numeric(19, 0) is thus
-9999999999999999999.0 to
9999999999999999999.0
But sometimes also
9999999999999999999.2
will be allowed from my experiments, don't know why.

Appropriate datatype for holding percent values?

What is the best datatype for holding percent values ranging from 0.00% to 100.00%?
Assuming two decimal places on your percentages, the data type you use depends on how you plan to store your percentages:
If you are going to store their fractional equivalent (e.g. 100.00% stored as 1.0000), I would store the data in a decimal(5,4) data type with a CHECK constraint that ensures that the values never exceed 1.0000 (assuming that is the cap) and never go below 0 (assuming that is the floor).
If you are going to store their face value (e.g. 100.00% is stored as 100.00), then you should use decimal(5,2) with an appropriate CHECK constraint.
Combined with a good column name, it makes it clear to other developers what the data is and how the data is stored in the column.
Hold as a decimal.
Add check constraints if you want to limit the range (e.g. between 0 to 100%; in some cases there may be valid reasons to go beyond 100% or potentially even into the negatives).
Treat value 1 as 100%, 0.5 as 50%, etc. This will allow any math operations to function as expected (i.e. as opposed to using value 100 as 100%).
Amend precision and scale as required (these are the two values in brackets columnName decimal(precision, scale). Precision says the total number of digits that can be held in the number, scale says how many of those are after the decimal place, so decimal(3,2) is a number which can be represented as #.##; decimal(5,3) would be ##.###.
decimal and numeric are essentially the same thing. However decimal is ANSI compliant, so always use that unless told otherwise (e.g. by your company's coding standards).
Example Scenarios
For your case (0.00% to 100.00%) you'd want decimal(5,4).
For the most common case (0% to 100%) you'd want decimal(3,2).
In both of the above, the check constraints would be the same
Example:
if object_id('Demo') is null
create table Demo
(
Id bigint not null identity(1,1) constraint pk_Demo primary key
, Name nvarchar(256) not null constraint uk_Demo unique
, SomePercentValue decimal(3,2) constraint chk_Demo_SomePercentValue check (SomePercentValue between 0 and 1)
, SomePrecisionPercentValue decimal(5,2) constraint chk_Demo_SomePrecisionPercentValue check (SomePrecisionPercentValue between 0 and 1)
)
Further Reading:
Decimal Scale & Precision: http://msdn.microsoft.com/en-us/library/aa258832%28SQL.80%29.aspx
0 to 1 vs 0 to 100: C#: Storing percentages, 50 or 0.50?
Decimal vs Numeric: Is there any difference between DECIMAL and NUMERIC in SQL Server?
I agree with Thomas and I would choose the DECIMAL(5,4) solution at least for WPF applications.
Have a look to the MSDN Numeric Format String to know why :
http://msdn.microsoft.com/en-us/library/dwhawy9k#PFormatString
The percent ("P") format specifier multiplies a number by 100 and converts it to a string that represents a percentage.
Then you would be able to use this in your XAML code:
DataFormatString="{}{0:P}"
If 2 decimal places is your level of precision, then a "smallint" would handle this in the smallest space (2-bytes). You store the percent multiplied by 100.
EDIT: The decimal type is probably a better match. Then you don't need to manually scale. It takes 5 bytes per value.
Use numeric(n,n) where n has enough resolution to round to 1.00. For instance:
declare #discount numeric(9,9)
, #quantity int
select #discount = 0.999999999
, #quantity = 10000
select convert(money, #discount * #quantity)

Why can't I enter an integer value into a decimal field?

I'm trying to write an insert statement for a SQL Server table that inserts the value 1 into a decimal field. The field is of the type decimal(10, 10) which, as far as I understand, means that it can have up to 10 digits altogether, and up to 10 of those digits can be after the decimal point. But, when I try to run the insert statement I get the following error:
Arithmetic overflow error converting int to data type numeric.
If I change the data type of the field to decimal(11, 10), it suddenly works. What am I not understanding here? What am I doing wrong?
decimal(10, 10) means all decimal places, no digits to the left of the decimal point!
see here: http://msdn.microsoft.com/en-us/library/aa258832(SQL.80).aspx_
decimal[(p[, s])]
p (precision) Specifies the maximum total number of decimal digits
that can be stored, both to the left
and to the right of the decimal point.
The precision must be a value from 1
through the maximum precision. The
maximum precision is 38. The default
precision is 18.
s (scale) Specifies the maximum number of decimal digits that can be
stored to the right of the decimal
point. Scale must be a value from 0
through p. Scale can be specified only
if precision is specified. The default
scale is 0; therefore, 0 <= s <= p.
Maximum storage sizes vary, based on
the precision.
decimal(11,10) gives you 1 digit the the left of the decimal and 10 to the right, so integer 1 fits now!
EDIT
when using: decimal(p,s), think of p as how many total digits (regardless of left or right of the decimal point) you want to store, and s as how many of those p digits should be to the right of the decimal point.
DECIMAL(10,5)= 12345.12345
DECIMAL(10,2)= 12345678.12
DECIMAL(10,10)= .1234567891
DECIMAL(11,10)= 1.1234567891

Resources