How to truncate decimal space in TSQL? - sql-server

I am trying to get the size of database and wanna display it to the GUI. Thats what i am using
select
sum(((((CAST(saf.[size] as DECIMAL(18,4)) * 8192) /1024) /1024) /1024)) as 'Size'
from sys.sysdatabases sdb
inner join sys.sysaltfiles saf
on sdb.dbid = saf.dbid
where sdb.name = 'testDB'
Result is :-
3.8329467773437500000
But i want it to be trimmed or round up like this :-
3.84

Try rounding instead.
http://msdn.microsoft.com/en-us/library/ms175003.aspx
I didn't see your unique rounding issue. This looks interesting:
http://support.microsoft.com/kb/196652

CAST(
CEILING (
sum(((((CAST(saf.[size] as DECIMAL(18,4)) * 8192) /1024) /1024) /1024))
* 100)
AS decimal(18,2)
) / 100
3.8329467773437500000
-> 383.29467773437500000 (* 100)
-> 384.00000000... (CEILING)
-> 384.00 (to decimal 18, 2)
-> 3.84 (/ 100)

Related

SQL Server batch and select statement output difference in decimal point

Following batch statement if i execute in SSMS giving me Result like given below
DECLARE #M_TUBE_VOLUME NUMERIC(38,10),
#M_TUBE_OD NUMERIC(38,10)=12.50000,
#M_TUBE_ID NUMERIC(38,10)=12.50000,
#M_TUBE_LEN NUMERIC(38,10)=4000.00000,
#M_TUBE_COUNT NUMERIC(38,10)=212.4215000,
#M_S_TUBE_LEN NUMERIC(38,10)=0.0000,
#M_S_TUBE_COUNT NUMERIC(38,10)=3587.000
SET #M_TUBE_VOLUME=(SELECT 3.141592 / 4 * #M_TUBE_OD * #M_TUBE_ID * ((#M_TUBE_LEN * #M_TUBE_COUNT) + (#M_S_TUBE_LEN * #M_S_TUBE_COUNT)));
SELECT #M_TUBE_VOLUME
RESULT -- 104272138.7104680000
and if i execute same thing in SSMS using select statement
SELECT 3.141592 / 4 * 12.50000 * 12.50000 * ((4000.00000 * 212.4215000) + (0.0000 * 3587.000))
RESULT -- 104272138.285625000000000000000
Why two result are different any reason please help me
it has something to do with rounding. As your computation involved numbers with decimal places.
Please refer to documentation Precision, scale, and Length (Transact-SQL)
I have extracted the relevant portion here
Operation Result precision Result scale *
e1 * e2 p1 + p2 + 1 s1 + s2
e1 / e2 p1 - s1 + s2 + max(6, s1 + p2 + 1) max(6, s1 + p2 + 1)
The result precision and scale have an absolute maximum of 38. When a result precision is greater than 38, it's reduced to 38, and the
corresponding scale is reduced to try to prevent truncating the
integral part of a result. In some cases such as multiplication or
division, scale factor won't be reduced, to maintain decimal
precision, although the overflow error can be raised.
In your second query, numbers like 3.141592 is taking as numeric(7,6) and 12.50000 is numeric(7,5) and in your first query, all those are numeric(38,10)
The rounding occurs on the first 4 expression.
I use SELECT INTO a temp table and then query the data type of the column in the temp table
SELECT f1 = 3.141592 / 4 * #M_TUBE_OD * #M_TUBE_OD,
f2 = 3.141592 / 4 * 12.50000 * 12.50000
INTO #t
select c.name, t.name, c.precision, c.scale
from tempdb.sys.columns c
inner join master.sys.systypes t on c.system_type_id = t.xtype
where object_id = object_id('tempdb..#t')
name name percision scale
f1 numeric 38 6
f2 numeric 25 18
You can see that for your first query, the result scale is reduced to only 6. So for first query, the result of the first 4 expression is 122.718438 (rounded to 38) rather than 122.7184375.
You over define the precision of your numeric. Reduce it and try
In the second version, you haven't explicitly defined your data type and it has defaulted to FLOAT.
You can verify this by changing your query to
DECLARE #M_TUBE_VOLUME FLOAT,
#M_TUBE_OD FLOAT=12.50000,
#M_TUBE_ID FLOAT=12.50000,
#M_TUBE_LEN FLOAT=4000.00000,
#M_TUBE_COUNT FLOAT=212.4215000,
#M_S_TUBE_LEN FLOAT=0.0000,
#M_S_TUBE_COUNT FLOAT=3587.000
SET #M_TUBE_VOLUME=(SELECT 3.141592 / 4 * #M_TUBE_OD * #M_TUBE_ID * ((#M_TUBE_LEN * #M_TUBE_COUNT) + (#M_S_TUBE_LEN * #M_S_TUBE_COUNT)));
SELECT #M_TUBE_VOLUME

Issue while adding values in SQL Server

Please read again till end (description updated)
I want something like this.
ex :
if (7200 / 42) is float then
floor(7200/42) + [7200 - {(floor(7200/42)) * 42}] / 10 ^ length of [7200 - {(floor(7200/42)) * 42}]
STEP : 1 => 171 + ((7200 - (171*42))/10 ^ len(7200-7182))
STEP : 2 => 171 + ((7200 - 7182)/10 ^ len(18))
STEP : 3 => 171 + (18/10 ^ 2)
STEP : 4 => 171 + (18/100)
STEP : 5 => 171 + 0.18
STEP : 6 => 171.18
I have written the code in SQL which actually works perfectly but the addition of 171 + 0.18 only gives 171
IF I can get "171/18" instead of "171.18" as string then it'd also be great. (/ is just used as separator and not a divison sign)
Following is the code I written
Here,
(FAP.FQTY + FAP.QTY) = 7200,
PRD.CRT = 42
(values only for example)
select
case when PRD.CRT <> 0 then
case when (FAP.FQTY + FAP.QTY)/PRD.CRT <> FLOOR((FAP.FQTY + FAP.QTY)/PRD.CRT) then --DETERMINE WHETHER VALUE IS FLOAT OR NOT
(floor((FAP.FQTY + FAP.QTY)/PRD.CRT)) +
((FAP.FQTY + FAP.QTY) - floor((FAP.FQTY + FAP.QTY)/PRD.CRT) * PRD.CRT) /
POWER(10, len(floor((FAP.FQTY + FAP.QTY) - floor((FAP.FQTY + FAP.QTY)/PRD.CRT) * PRD.CRT)))
else
(FAP.FQTY + FAP.QTY)/PRD.CRT -- INTEGER
end
else
0
end
from FAP inner join PRD on FAP.Comp_Year = PRD.Comp_Year and
FAP.Comp_No = PRD.Comp_No and FAP.Prd_Code = PRD.Prd_Code
I got all the values correct till 171 + 0.1800 correct but after that I am only receiving 171 in the addition. I want exactly 171.18.
REASON FOR THIS CONFUSING CALCULATION
Its all about accounting
Suppose, a box(or a cartoon) has 42 nos. of items.
A person sends 7200 items. how many boxes he has to send?
So that will be (7200/42) = 171.4257.
But boxes cannot be cut (its whole number i.e 171).
so 171 * 42 ie 7182 items.
Remaining items = 7200 - 7182 = 18.
So answer is 171 boxes and 18 items.
In short 171.18 or "171/18"
Please help me with this..
Thank you in advance.
Recognise that you're not producing an actual numeric result, I'd describe it as unhealthy to try to keep it using such a datatype1.
This produces the strings you're seeking, if I've understood your requirement:
;With StartingPoint as (
select 7200 as Dividend, 42 as Divisor
)
select
CONVERT(varchar(10),Quotient) +
CASE WHEN Remainder > 0 THEN '.' + CONVERT(varchar(10),Remainder)
ELSE '' END as FinalString
from
StartingPoint
cross apply
(select Dividend/Divisor as Quotient, Dividend % Divisor as Remainder) t
(Not tested for negative values. Some adjustments may be required. Technically % computes the modulus rather than the remainder, etc)
1Because someone might try and add two of these values together and I doubt that produces a correct result, not even necessarily if using the same Divisor to compute both.
Just another idea about how to calculate it.
Simple calculate the whole boxes.
And concatinate a dot with the remaining items (using a modulus).
Wrapped it all up in a CASE WHEN (or IIF) to avoid the divide by zero.
Example snippet:
declare #TestTable table (FQTY numeric(18,2), QTY numeric(18,2), CRT numeric(18,0));
insert into #TestTable (FQTY,QTY,CRT) values
(5000, 2200, 42),
(5000, 2200, 0),
( 100, 200, 10);
select *,
(CASE
WHEN CRT>0
THEN CONCAT(CAST(FLOOR((FQTY+QTY)/CRT) as INT),'/',CAST((FQTY+QTY)%CRT as INT))
ELSE '0'
END) AS Boxes
from #TestTable;
Result:
FQTY QTY CRT Boxes
------- ------- --- ------
5000.00 2200.00 42 171/18
5000.00 2200.00 0 0
100.00 200.00 10 30/0
The CONCAT returns a varchar, and so does the CASE WHEN.
But you could wrap that CASE WHEN in a CAST.
You're getting an automatic type conversion from int to decimal(10,0) which is probably not what you want.
https://learn.microsoft.com/en-us/sql/t-sql/data-types/int-bigint-smallint-and-tinyint-transact-sql?view=sql-server-2017
Check out the "Caution" box.
If you want a specific amount of precision, you'll need to explicitly cast() the values to the desired data type.
if i understand your logic correctly you want the remainder of 7200 divide by 42 and the remainder is to divide by 100
declare
#dividend int = 7200,
#divisor int = 42
select (#dividend / #divisor)
+ convert(decimal(10,4),
(#dividend % #divisor) * 1.0 / power(10, len(#dividend % #divisor)))
EDIT: change to handle the 10^len(remainder)

Simple SELECT query with calculated columns returns error

I have a query:
use [Some_Database];
go
select toip 10 IDEVENT,
COUNT(*)
from [AL_PROTOCOL]
group by IDEVENT
order by Quanity desc
go
In output I get results as
IDEVENT Quanity
664 4,037787E+07
566 2,124254E+07
438 1,248467E+07
294 9926404
564 9777449
436 5784661
310 5709771
428 5161083
432 5154893
434 5150308
So, then I try to calculate second (*60 / 1,000,000) column using CONVERT (real (100), COUNT(*)*60/1000000, 2) as 'Size, Mb'
next way:
use [Some_Database];
go
select top 10 IDEVENT,
COUNT(*),
CONVERT (real (100), COUNT(*)*60/1000000, 2) as 'Size, Mb'
from [AL_PROTOCOL]
group by IDEVENT
order by Quanity desc
go
After this, I get this error
8115 'Arithmetic overflow error converting expression to int type
numeric'.
Technet explains, that this kind of errors occurs when a value in the column is overgrowing the maximal size of integer type. But, why doesn't it convert in REAL type?
Also, this question solved by replacing '*60/1,000,000' on '/1,000,000*60', but my interest is not satisfied.
What am I doing wrong? thanks
Look carefully at the order of operations you have asked the DBMS to perform. CONVERT (real (100), COUNT(*)*60/1000000, 2) means:
COUNT(*)
result * 60
result / 1000000
CONVERT (real (100), result, 2)
Until step 4, the value is still an integer, so you get an overflow error if COUNT(*) * 60 is higher than the maximum representable integer.
Because * and / have the same precedence, the workaround you found is CONVERT (real (100), COUNT(*)/1000000*60, 2), which means:
COUNT(*)
result / 1000000
result * 60
CONVERT (real (100), result, 2)
We're still doing maths on integers, but now we never overflow because we divide before multiplying.
But what you actually wanted was to do all the maths on the floating point value:
COUNT(*)
CONVERT (real (100), result, 2)
result * 60
result / 1000000
For that, you just need to swap the nesting around so that you convert the COUNT(*) result directly, and then apply the maths: CONVERT (real (100), COUNT(*), 2) * 60 / 1000000
If you multiply a really large number by 60, you're going to get an even bigger number, increasing the chances of an arithmetic overflow. The fact that you divide it by 1,000,000 after is too late, you've already tried to generate a number that is too big to proceed with.
However, when you divide it by 1,000,000 first before multiplying by 60, then you are always going to get a smaller number, so you should never get an arithmetic overflow.
Also, with your CONVERT, you are doing it on the entire result, and all of the numbers in the calculation are of INT types, so it will generate an INT before the conversion.
COUNT(*)*60/1000000 -- COUNT(*) is an INT, as well as the other numbers
If you change the ordering of the CONVERT \ CAST it should work.
DECLARE #number AS INT
SET #number = 123456789
-- this doesn't work
SELECT CAST((#number * 60) AS REAL(100))
-- this does
SELECT CAST(#number AS REAL(100)) * 60

Rounding the value to the nearest 50

I am trying to round down the value to the nearest 50.
1-50 it should round down to below 00 and when its 51-rest then it should round down to 50
ex:
245 (until 1-49) its should round down to 200
258 (from 50-99)then it should round down to 250
I tried this,its wrking good but I need smething other than case statement
#ResultAmount = ROUND(#ResultAmount, -2, 1) +
CASE WHEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2) IN (00, 50)
THEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2)
WHEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2) BETWEEN 1 AND 49
THEN 00
WHEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2) BETWEEN 51 AND 99
THEN 50
END
Thanks in advance!!!
This is all you need
SELECT FLOOR(#ResultAmount / 50) * 50;
e.g below
declare #ResultAmount decimal(10,2) = 249;
SELECT FLOOR(#ResultAmount / 50) * 50;
SET #ResultAmount = 250;
SELECT FLOOR(#ResultAmount / 50) * 50;
SET #ResultAmount = 200;
SELECT FLOOR(#ResultAmount / 50) * 50;
SET #ResultAmount = 199;
SELECT FLOOR(#ResultAmount / 50) * 50;
It sounds like numbers 0-50 get rounded up to "50", but any number larger than that should just get rounded to the nearest 50. Something like the following should work:
(CASE WHEN f1/50 < 1 THEN 1 ELSE ceiling(f1/50) END) * 50 AS rounded_to_50
You can simply divide the number by 50, round then multiply by 50 again, eg:
select cast(round(#i/50.0,0)*50 as int)
This will return 500 if #i is 524 but 550 if #i is 525.
You can create a function to make this easier:
create function fn_Round_By(#input int,#divider float)
returns int
as
begin
RETURN (cast(round(#input/#divider,0)*#divider as int));
end
Again, select dbo.fn_Round_By(525,50) returns 550 andselect dbo.fn_Round_By(524,50)` returns 500.
If you want values less than 50 to round up to 50, you can use a simple CASE, eg:
create function fn_Round_By(#input int,#divider float)
returns int
as
begin
RETURN (
CASE
WHEN #input <=#divider then #divider
else cast(round(#input/#divider,0)*#divider as int)
END
);
end
Rounding down is performed by the FLOOR function so a function that rounded down to a specific interval would be:
create function fn_Floor_By(#input int,#divider float)
returns int
as
begin
RETURN (cast(FLOOR(#input/#divider)*#divider as int));
end
or, preserving the logic that rounds up anything under 50:
create function fn_Floor_By(#input int,#divider float)
returns int
as
begin
RETURN (
CASE
WHEN #input <=#divider then #divider
else cast(FLOOR(#input/#divider)*#divider as int)
END
);
end
You can calculate modulo 50 and use this to reduce the original value
DECLARE #ResultAmount int = 243
SELECT #ResultAmount - (#ResultAmount%50)

rounding two math calculations don't add up like they should

Why doesn't this
select round(17 * .235, 2), round(17 * (1-.235), 2)
return
4.00 and 13.00
instead of
4.00 and 13.01
Also,
select CONVERT(Decimal(19,2), 17 * .235), CONVERT(Decimal(19,2), 17 * (1-.235))
returns
4.00 and 13.01
How can I force the two calculations to add up to the starting number (17.00)?
select cast(17 * .235 as float)
,cast(17 * (1-.235) as float)
,cast(17 * .235 as float)+cast(17 * (1-.235) as float)
Returns
(No column name) (No column name) (No column name)
3.995 13.005 17
If you don't like float, try decimal (some folks hate float, me not so much)
select cast(17 * .235 as decimal(18,9))
,cast(17 * (1-.235) as decimal(18,9))
,cast(17 * .235 as decimal(18,9))+cast(17 * (1-.235) as decimal(18,9))
I think the answer is that SQL rounds AWAY FROM ZERO. Which is different from VB's rounding (ROUND TOWARDS EVEN). At least that's the way I read this:
http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/sql-server-rounding-methods/

Resources