Wrong calculation of values after applying the round off in SQL Server - sql

have three values a,b,c with datatype Decimal(15,7).
a is a summation of values b and c.
I didn't get what is wrong. What is possible way to get right result after rounding?
For example:
Declare #a decimal(15,7),
#b decimal(15,7),
#c decimal(15,7)
SET #a = '15212.82856500000'
SET #b = '15207.52909500000'
SET #c = '5.29947000000'
In above condition it returns the right result
select #a, #b + #c
But I used the round off 3 to all values it returns incorrect result.
select ROUND(#a, 3), ROUND(#b, 3) + ROUND(#c, 3)
Expected result is
ROUND(#a, 3) = ROUND(#b, 3) + ROUND(#c, 3)

You should round the result, not each variable individually:
SELECT ROUND(#a + #b + #c, 3)

The exact query should be
select ROUND(#a,3),ROUND(#b+#c,3)
This can gives you the desired result.

Each time you ROUND(), you deviating from the actual by a certain percentage, however tiny. Now the more you use the ROUND(), the further away you are deviating. It is always advisable to ROUND the final output.
I'm assuming the example numbers that you gave are not the actual numbers you're having issues with ?

Related

How can we read a varchar column, take the integer part out and add new column incrementing that integer part using script

I need to write a SCRIPT for below scenario:
We have a column X with rows value for this column X as X01,X02,X03,X04........
The problem I am stuck with is that I needed to add another row to this table based on the value of the last row that is X04, Well I am able to identify the logic that I need to work which is given below:
I need to read value X04
Take the integer part 04
Increment by 1 => 05
Save column value as X05
I am able to pass with the 1st step which is not very hard. The problem that I am facing is the next steps. I have researched and tried quite a lot commands but none worked.
Any help is highly appreciated. Thanks.
You seem to be describing:
select concat(left(max(x), 1),
right(concat('00', try_convert(int, right(max(x), 2)) + 1), 2)
from t;
This is doing the following:
Taking the left most character.
Converting the two right characters to a number and adding one.
Converting that back to a zero-padded string.
Here is a db<>fiddle.
Now: That you want to increment a string value seems broken. You should just use an identity column or sequence to assign a number. You can format the value as a string when you query the table -- or use a computed column to store that.
Try below Script
CREATE TABLE #table (x varchar(20))
INSERT INTO #table VALUES('X01'),('X02'),('X03'),('X04')
DECLARE #maxno NVARCHAR(20)
DECLARE #maxstring NVARCHAR(20)
DECLARE #finalno NVARCHAR(20)
DECLARE #loopminno INT =1 -- you can change based on the requirement
DECLARE #loopmaxno INT =10 -- how many number we want to increment
WHILE #loopminno < #loopmaxno
BEGIN
select #maxno = MAX(CAST(SUBSTRING(x, PATINDEX('%[0-9]%', x), 100) as INT))
, #maxstring = MAX(SUBSTRING(x, 1, PATINDEX('%[0-9]%',x)-1))
from #table
where PATINDEX('%[1-9]%',x)>0
SELECT #finalno = #maxstring + CASE WHEN CAST(#maxno AS INT)<9 THEN '0' ELSE '' END + CAST(#maxno+1 AS VARCHAR(20))
INSERT INTO #table
SELECT #finalno
SET #loopminno = #loopminno+1
END

SQL Server float unstable last digits

In SQL Server I create an aggregated column (a combination of other columns that I add, multiple, sum etc) which is of SQL datatype float.
However, when I run the same query multiple times, the last 2 digits of my float are unstable and keep changing.
Below the floats I get with the random last two digits - I try to convert to decimal and then chop off the last two digits.
select round(convert(decimal(20,19), 0.0020042890676442646), 17,1)
select round(convert(decimal(20,19), 0.0020042890676442654), 17,1)
In SSMS the result for both is: 0.0020042890676442600 as expected.
Mind you, the input constants here i took from python, so they might have been modified already. I can't take them from sql directly, as it is incredibly rare to get the calculation anomaly and i don't know how to reproduce it.
But running this via pypyodbc to python, sometimes the result is a python decimal.Decimal type with value 0.0020042890676442700 for the second statement, so it does seem to do rounding rather than truncation.
I have also noticed that the result of the calculation in sql is not always the same, and there is instability there in the last digit of the float - not sure how to test this sytematically though.
The constants casted to floats give:
select convert(float,0.0020042890676442646)
select convert(float,0.0020042890676442654)
Result: 0.00200428906764427.
Wrapped in decimals and rounded:
select round(convert(decimal(20,19), convert(float,0.0020042890676442646)), 17,1)
select round(convert(decimal(20,19), convert(float,0.0020042890676442654)), 17,1)
The result in SSMS is: 0.0020042890676442700 in both cases.
I tried sending back the floats directly instead of casting to decimal, but it seems the two unstable digits are always added at the end when they reach python. Even truncating doesn't help, other random numbers are then added.
It almost seems as if python modifies both float and Decimal during transport in a random manner, or that the instability is in sql already or both.
I tried truncating the np.float64 on the python side like this: Truncating decimal digits numpy array of floats
but as the last float digit in sql can be between e15 and e19 I can't set a consistent truncate level unless i floor everything at e15.
The order of processing of an aggregate is undefined, in the same way that the order of the results of any query are undefined unless you use an ORDER BY clause. In the case of floats, order matters. Order of aggregate processing can be forced using an OVER clause. Here's some code to demonstrate:
-- demonstrate that order matters when adding floats
declare #a float
declare #b float
declare #c float
declare #d float
declare #e float
set #a = 1
set #b = 1
set #c = 9024055778268167
-- add A to B, and then add C
-- result is 9024055778268170
set #d = #a + #b
set #e = #d + #c
select cast( #e as decimal(38,0) )
-- add C to B, and then add A
-- result is 9024055778268168
set #d = #c + #b
set #e = #d + #a
select cast( #e as decimal(38,0) )
-- put these values into a table
create table OrderMatters ( x float )
insert into OrderMatters ( x ) values ( #a )
insert into OrderMatters ( x ) values ( #b )
insert into OrderMatters ( x ) values ( #c )
declare #x float
-- add them in ascending order
-- result is 9024055778268170
select #x = sum(x) over (order by x asc ) from OrderMatters
select cast(#x as decimal(38,0))
-- add them in descending order
-- result is 9024055778268168
select #x = sum(x) over (order by x desc ) from OrderMatters
select cast(#x as decimal(38,0))

sql datatype rounding

I am having the below query.
declare
#a NUMERIC(20,4)
set #a=24900*0.3333333333
select #a
I am expecting 8299.999999 but it gives 8300.00. Can some body help me on this?
Your result is rounded as you have only 4 decimals at your disposal. Use
set #a = round(24900*0.3333333333, 4, 1)
This will not round but truncate the result.
I do not know if that is what you want, because a value like 0.12346 will be truncated to 0.1234 and not rounded to 0.1235.
you may want this or something like this:
declare #a NUMERIC(20,6)
set #a=cast(24900 as numeric(20,6))*cast(0.3333333333 as numeric(20,10))
select #a

Padding numeric on SQL SYBASE

I have a numeric field like 1,3065 and I need that to became like this: 000000000000000130.
I mean 16 integers and 2 decimals, without the comma, adding 0es to the left and, if needed, to the right.
Is there any way to do that with a query?
I think this will work in Sybase:
select right(replicate('0', 16) + cast(cast(field*100 as int) as varchar(255)), 16)
Try this way:
select REPLICATE('0',16-len(cast(cast((1.3065*100) as int) as varchar(16))))+cast(cast((1.3065*100) as int) as varchar(16))
Result of above select:
0000000000000130
If you want approximation you should use FLOOR or CEILING function as below
declare #multiplier int
select #multiplier = 1000
select REPLICATE('0',16-len(cast((floor(1.3065*#multiplier)) as varchar(16))))+cast(floor(1.3065*#multiplier) as varchar(16))
Result of above select:
0000000000001306
You can change an approximation by changing #multiplier. For example if you want result from 1st query you should change #multiplier to 100.

Most efficient method for adding leading 0's to an int in sql

I need to return two fields from a database concatenated as 'field1-field2'. The second field is an int, but needs to be returned as a fixed length of 5 with leading 0's. The method i'm using is:
SELECT Field1 + '-' + RIGHT('0000' + CAST(Field2 AS varchar),5) FROM ...
Is there a more efficient way to do this?
That is pretty much the way: Adding Leading Zeros To Integer Values
So, to save following the link, the query looks like this, where #Numbers is the table and Num is the column:
SELECT RIGHT('000000000' + CONVERT(VARCHAR(8),Num), 8) FROM #Numbers
for negative or positive values
declare #v varchar(6)
select #v = -5
SELECT case when #v < 0
then '-' else '' end + RIGHT('00000' + replace(#v,'-',''), 5)
Another way (without CAST or CONVERT):
SELECT RIGHT(REPLACE(STR(#NUM),' ','0'),5)
If you can afford/want to have a function in your database you could use something like:
CREATE FUNCTION LEFTPAD
(#SourceString VARCHAR(MAX),
#FinalLength INT,
#PadChar CHAR(1))
RETURNS VARCHAR(MAX)
AS
BEGIN
RETURN
(SELECT Replicate(#PadChar, #FinalLength - Len(#SourceString)) + #SourceString)
END
I would do it like this.
SELECT RIGHT(REPLICATE('0', 5) + CAST(Field2 AS VARCHAR(5),5)
Not necessarily all that "Easier", or more efficient, but better to read. Could be optimized to remove the need for "RIGHT"
If you want to get a consistent number of total strings in the final result by adding different number of zeros, here is a little bit modification (for vsql)
SELECT
CONCAT(
REPEAT('0', 9-length(TO_CHAR(var1))),
CAST(var1 AS VARCHAR(9))
) as var1
You can replace 9 by any number for your need!
BRD