SQL Server : divide by zero error - sql-server

I have searched the forums and found several responses on this topic but as I am new to SQL I'm not getting it.
I created a TSQL query that when run returns "Divide by zero error". This is because I am dividing one column by another and there is a zero in one of the records. Ok, I got that part but I can't make heads or tails of the posts explaining how to resolve the issue.
SELECT
f.Sales_Rep1 AS 'Sales Rep'
, SUM(CAST(f.Other1_Revenue + f.Other2_Revenue AS FLOAT)) AS 'MRR'
, CW.MRR_Goal AS 'MRR Goal'
, SUM((f.Other1_Revenue + f.Other2_Revenue)/(CW.MRR_Goal)) AS 'Total'
, SUM(CAST(f.Product_Revenue + f.Service_Revenue AS FLOAT)) AS 'NRR'
, CW.NRR_Goal AS 'NRR Goal'
, (cw.MRR_Goal + cw.NRR_Goal)AS 'Total Goal'
FROM
dbo.v_rpt_Opportunity AS f
INNER JOIN
dbo.v_memberpickerlist AS m ON f.Sales_Rep1 = m.Member_ID
INNER JOIN
dbo.CW_SalesGoals AS CW ON CW.Sales_Rep = f.Sales_Rep1
WHERE
(f.Expected_Close_Date >= DATEADD(MM, DATEDIFF(MM, 0, GETDATE()), 0))
AND (m.activestatus = 'active') AND (f.Status = 'Won')
OR (f.Expected_Close_Date >= DATEADD(MM, DATEDIFF(MM, 0, GETDATE()), 0))
AND (m.activestatus = 'active')
AND (f.Status LIKE '%submitted%')
GROUP BY
f.Sales_Rep1, CW.MRR_Goal, CW.NRR_Goal,
f.Other1_Revenue, f.Other2_Revenue
I know the line that is causing the issue, but I do not know how to resolve it...
SUM((f.Other1_Revenue + f.Other2_Revenue) / (CW.MRR_Goal)) AS 'Total'
What I am trying to do is get the percentage of the goal but I cant get past the division to do that.
Any help would be greatly appreciated. Thank you!

You can use the nullif() function to avoid divide by zero exceptions:
(f.Other1_Revenue + f.Other2_Revenue) / nullif(CW.MRR_Goal, 0)
In the event that CW.MRR_Goal is 0, the result for the entire expression will be null1.
Sample:
select 1 / nullif(0, 0) -- null
1 I've selected null because, in my opinion, it's the most appropriate analogue for undefined in SQL. While that's helpful for an individual expression, it may not be what you're looking for in an aggregate sense.

Use a CASE statement:
SUM(CASE WHEN CW.MRR_Goal <> 0 THEN (f.Other1_Revenue + f.Other2_Revenue)/(CW.MRR_Goal) ELSE 0 END) AS 'Total'
This way, it will perform the division only if the value of CW.MRR_Goal in that record is other than zero; if it's zero, it will return 0

You could use an IIF:
SUM(IIF(CW.MRR_Goal = 0, 0, (f.Other1_Revenue + f.Other2_Revenue) / (CW.MRR_Goal))
This way if the goal is equal to 0 then it will SUM the 0 rather than trying the division whereas if the goal is not equal to 0 then it will do the division.
More on IIF https://msdn.microsoft.com/en-GB/library/hh213574.aspx
As user3540365 mentioned IIF only applies from SQL Server 2012 as opposed to CASE which applies from SQL Server 2008 onwards see here and is an SQL standard.

Related

i used nullif and got sql divide by zero error

select STORECODE,
ItemCode,
ColorCode,
ToplamStok,
ToplamSatis,
(CASE WHEN ToplamSatis = 0
THEN ISNULL(ToplamStok/NULLIF(ToplamSatis,0.1)*7,0)
ELSE (ToplamStok/ToplamSatis)*7
end) as SDH
into #SatisStokSDH
from #SatisStok
for this query, I'm getting this error : Divide by zero error encountered.
why am i getting this, i already used isnull function?
Thanks in advance.
Your problem is that when ToplamSatis happens to be zero, you are still dividing by zero:
NULLIF(ToplamSatis, 0.1)
The above would replace ToplamSatis with 0.1, but only if the former were NULL, not if it were zero. Try the following CASE logic:
CASE WHEN ToplamSatis = 0
THEN ISNULL((ToplamStok / 0.1)*7, 0) -- not sure if need to wrap with ISNULL
ELSE (ToplamStok / ToplamSatis)*7
END

Multiply columns in T-SQL join query

I am using SQL Server 2008. I am trying to build a T-SQL query to calculate some performance metrics based on data from several tables. Unfortunately, I am stuck on one of the calculations and can not figure out what is wrong. I would greatly appreciate any help:
The calculations require the total produced (from table ShiftHourCounts), the total scrap pieces (from table ShiftReportScrap), and the total downtime (from Downtime query)
I have tried to add each operation/calculation in the query mainly for my own education/ troubleshooting
I do not understand why column Q returns zero. Q= Tok/Tp, and the query correctly returns and calculates both Tok and Tp individually. In the below example, Q should =0.994
The query currently returns correct values for everything except Q, A, P, and OEE. Q, A, P, and OEE always return zero
Current query:
--OEE= A*P*Q (this is the final desired result/ calculation)
--A= (Planned run time - Unplanned Down Time)/Planned run time
--A= (Prt - Dtu)/Prt
--Prt= Maximum Available Time - Planned Down Time
--Prt= Mat=DTp
--Effective production time= Planned run time - Unplanned Down Time
--Ept=Prt-DTu
--P= (BDT*total number of produced parts)/Effective production time
--P= (BDT*Tp)/Ept
--Q= Total number of OK parts/Total number of produced parts
--Q= Tok/Tp
select
sm.SR_ID, sm.SR_PartID, sm.SR_StartTime,
isnull(sm.SR_EndTime,GETDATE()) AS EndTime,
isnull(sm.SR_BDT,1) AS BDT,
DATEDIFF(n, sm.SR_StartTime, isnull(sm.SR_EndTime, GETDATE())) AS Prt,
isnull(p.TotalProduced,0) AS Tp,
isnull(s.Scrap,0) AS Scrap,
(isnull(p.TotalProduced, 0) - isnull(s.Scrap, 0)) AS Tok,
isnull(dt.DownTimeDuration, 0) AS DTu,
((isnull(p.TotalProduced, 0) - isnull(s.Scrap, 0)) / isnull(p.TotalProduced, 0)) AS Q, --Q= Tok/Tp
((DATEDIFF(n, sm.SR_StartTime, isnull(sm.SR_EndTime, GETDATE())) - isnull(dt.DownTimeDuration, 0)) / DATEDIFF(n, sm.SR_StartTime, isnull(sm.SR_EndTime, GETDATE()))) AS A,
((isnull(sm.SR_BDT, 1) * isnull(p.TotalProduced, 0)) / (DATEDIFF(n, sm.SR_StartTime, isnull(sm.SR_EndTime, GETDATE())) - isnull(dt.DownTimeDuration, 0))) AS P,
(((isnull(p.TotalProduced, 0) - isnull(s.Scrap, 0)) / isnull(p.TotalProduced, 0)) * ((DATEDIFF(n, sm.SR_StartTime, isnull(sm.SR_EndTime, GETDATE())) - isnull(dt.DownTimeDuration, 0)) / DATEDIFF(n, sm.SR_StartTime, isnull(sm.SR_EndTime, GETDATE())))*((isnull(sm.SR_BDT,1)*isnull(p.TotalProduced,0))/(DATEDIFF(n,sm.SR_StartTime,isnull(sm.SR_EndTime,GETDATE()))-isnull(dt.DownTimeDuration,0)))) AS OEE
FROM
ShiftReportMaster sm
LEFT JOIN
(SELECT
SH_ShiftID, Sum(SH_Produced) AS TotalProduced
FROM
ShiftHourCounts
GROUP BY
SH_ShiftID) p ON (p.SH_ShiftID = sm.SR_ID)
LEFT JOIN
(SELECT
SRS_SR_ID, SRS_PartID, Sum(SRS_Scraped) AS Scrap
FROM
ShiftReportScrap
GROUP BY
SRS_SR_ID, SRS_PartID) s ON (s.SRS_SR_ID = sm.SR_ID)
AND (s.SRS_PartID = sm.SR_PartID)
LEFT JOIN
(SELECT
srd.DTR_SRID, [Downtime reasons].DT_Planned,
Sum(srd.DTR_DownTimeDuration) AS DownTimeDuration
FROM
ShiftReportDowntime srd
LEFT JOIN
[Downtime reasons] ON srd.DTR_Reason = [Downtime reasons].DT_ID
GROUP BY
srd.DTR_SRID, [Downtime reasons].DT_Planned
HAVING
((([Downtime reasons].DT_Planned) = 0))) dt ON (dt.DTR_SRID = sm.SR_ID)
WHERE
sm.SR_ID = 3689;
Most likely from integer division. Try this:
((isnull(p.TotalProduced+.0,0.0)-isnull(s.Scrap+.0,0.0))
/nullif(p.TotalProduced,0)) AS Q, --Q= Tok/Tp
Adding .0 or multiplying 1.0 implictly converts the integers into a decimal type.
Dividing integers will return an integer type, and if that value is less than 1 it will return 0 because it truncates instead of rounding or using some other logic to return an integer.

Divide function in SQL Server 2008

I'm trying to use the Divide function in SSAS:
https://msdn.microsoft.com/en-us/library/jj873944(v=sql.110).aspx
But I have to support SQL server 2008 and it looks like this is not available. The initial problem I am having is that when I add a case statement to a measure calculation the performance of the query is VERY poor. It's been suggested to use this divide function.
The statement to create member is:
Create Member CurrentCube.[Measures].[AvgSentiment] As
CASE
WHEN ([Measures].[SentimentCount]) > 0 THEN [Measures].[SentimentSum] / [Measures].[SentimentCount]
WHEN([Measures].[SentimentCount]) = 0 THEN 0
END
, VISIBLE =1
, ASSOCIATED_MEASURE_GROUP = 'vw_CUBE_FACT' ;
Which I tried replacing with:
Create Member CurrentCube.[Measures].[AvgSentiment] As
Divide ([Measures].[SentimentSum], [Measures].[SentimentCount], 0)
, VISIBLE =1
, ASSOCIATED_MEASURE_GROUP = 'vw_CUBE_FACT'
I also tried:
Create Member CurrentCube.[Measures].[AvgSentiment] As
IIF(
[Measures].[SentimentCount] = 0
, 0
, [Measures].[SentimentSum]/[Measures].[SentimentCount]
)
, VISIBLE =1
, ASSOCIATED_MEASURE_GROUP = 'vw_SEAMS_CUBE_FACT'
This also ate up a tonne of CPU / Memory.
Divide won't make a huge difference.
Your third attempt is almost what you want. What you aim to do with cube measures is make them "sparse". My interpretation of this is that you want them to only exist in the parts of the cube where they should exist - everywhere else the cube space should be empty - this is achieved by using null in your measure rather than 0:
CREATE MEMBER CurrentCube.[Measures].[AvgSentiment] As
IIF(
[Measures].[SentimentCount] = 0
, NULL //<<got rid of the 0
, [Measures].[SentimentSum]
/[Measures].[SentimentCount]
)
, VISIBLE =1
, ASSOCIATED_MEASURE_GROUP = 'vw_SEAMS_CUBE_FACT'
IIF generally performs better than CASE so better this than adapting your attempt using CASE.
If the above still performs badly then I suspect you need to investigate the performance of [Measures].[SentimentCount] - how is this calculated?

Setting hh:mm to 24 hours format in one single t-sql Query - SQL Server 2012

I got a query which returns some hh:ss time values. The problem however is that it returns it in a PM/AM format while it needs to be a 24 hours format. I can't change the global language setting because this 24 hours time setting is query specific.
I was wondering how to solve this issue?
The query I got now is as follows:
SELECT
dbo.qryMPDisplayPre.Datum, dbo.qryMPDisplayPre.Relatie,
dbo.qryMPDisplayPre.[Order], dbo.qryMPDisplayPre.Status,
dbo.WorkOrder.DeviceID, dbo.Relaties.RelatieNaam AS Monteur,
dbo.Orders.Omschrijving AS OrderOmschrijving,
Format(dbo.WorkOrder.WBTravelDeparture, 'hh:mm') AS TravelDeparture,
Format(dbo.WorkOrder.WBTravelArrival, 'hh:mm') AS TravelArrival,
Format(dbo.WorkOrder.WBWorkArrival, 'hh:mm') AS WorkArrival,
Format(dbo.WorkOrder.WBWorkDeparture, 'hh:mm') AS WorkDeparture,
(CASE WHEN WorkOrder.[WBtravelhours] IS NULL
THEN 0 ELSE (CAST(WorkOrder.[WBTravelHours] * 100.0 / 100.0 AS DECIMAL(30, 2))) END) AS TravelHours,
(CASE WHEN WorkOrder.[wbworkhours] IS NULL
THEN 0 ELSE (CAST(WorkOrder.[WBWorkHours] * 100.0 / 100.0 AS DECIMAL(30, 2))) END) AS WorkHours,
dbo.qryWBMontageGeboekt.Geboekt, dbo.Orders.OpdAdres,
dbo.Orders.OpdPC, dbo.Orders.OpdPlaats,
LEFT(dbo.Orders.Omschrijving, 9) AS Expr1
FROM
dbo.qryWBMontageGeboekt
RIGHT OUTER JOIN
dbo.Orders
RIGHT OUTER JOIN
dbo.Relaties
RIGHT OUTER JOIN
dbo.WorkOrder
RIGHT OUTER JOIN
dbo.qryMPDisplayPre ON dbo.WorkOrder.WONummer = dbo.qryMPDisplayPre.[Order]
AND dbo.WorkOrder.WOStatus = dbo.qryMPDisplayPre.Status
AND dbo.WorkOrder.WOAssignmentDate = dbo.qryMPDisplayPre.Datum
ON dbo.Relaties.RelatieNummer = dbo.qryMPDisplayPre.Relatie
ON dbo.Orders.Nummer = dbo.qryMPDisplayPre.[Order]
ON dbo.qryWBMontageGeboekt.Datum = dbo.qryMPDisplayPre.Datum
AND dbo.qryWBMontageGeboekt.Relatie = dbo.qryMPDisplayPre.Relatie
AND dbo.qryWBMontageGeboekt.[Order] = dbo.qryMPDisplayPre.[Order]
WHERE
(dbo.qryMPDisplayPre.Datum > '11/1/2012')
AND (dbo.qryMPDisplayPre.Status <> 0)
It is kinda weird since the values in WorkArrival are getting displayed correctly in the 24-hours format. Though the values in TravelDeparture, TravelArrival and WorkDeparture aren't while they are formatted the same way as the WorkArrival one.
So this made me believe that there was something wrong with the values from where they are fetched, the WorkOrder table. Though this table contains date times in a 24-hours way and they are all the same (so this couldn't be the problem).
See here the workorder table from where the values are fetched:
As you can see this are all dates with 24 hour HH:MM values.
Now below you can see the Query results with its PM/AM formatted time values:
As you can see the Query results are very weird. It seems that the WorkArrival fields returns its value correct, but the others don't. What is also strange is the fact that the field TravelDeparture returns some off its values correctly (2 top ones) but others incorrect..
Any clue how this can happen, and how I can let the values return in a 24 hours manor (in the query results).
In your example they should all be in 12 hour format, and I see no reason for it not being the case. The format for 12 hours is 'hh' and you are using it in all places.
Is this your original query? If not then check your format strings for upper / lower case. The format for 24 hours happens to be 'HH' (upper case instead of lower case being the only difference).

How to protect sql statement from Divide By Zero error

I'm in the process of creating some reports that take a finite total (lets say 2,500) of products (for this example lets say Ice Cream Cones) and counts how many of them were broken before serving.
Now the actual count code of broken cones I've got down.
SELECT COUNT(broken_cones) FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
However, I need a percentage of broken cones from this total as well. I've been playing around with the code but I keep running into a 'Divide By Zero' error with this code below.
SELECT CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS
decimal(7,4)) FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
For right now, there aren't any broken cones (and won't be for a while) so the total right now is zero. How can I show the NULL scenario as zero?
I tried to place an ISNULL statement in the mix but I kept getting the 'Divide by Zero' error. Am I even doing this right?
::edit::
Here's what I ended up with.
SELECT
CASE
WHEN COUNT(broken_cones) = 0 then 0
ELSE CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS decimal(7,4))
END
FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
Use a case statement.
SELECT
CASE WHEN COUNT(broken_cones) = 0 then 0
ELSE CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS decimal(7,4)) END
FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
You already have a solution, but this is why your original solution didn't work.
Your NULLIF needs to be moved in order to be effective. It is doing the division before it gets to the NULLIF call. Dividing by null will return a null value.
SELECT CAST((.01 * 2500)/NULLIF(Count(broken_cones), 0) AS decimal(7,4))
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'`
The NULLIF() function is a great way to prevent divide by zero, since anything divide by NULL returns null. The way to use it is as follows:
<expression> / NULLIF( <expression>, 0 )
Unfortunately you've wrapped your whole divide expression in NULLIF() which is why it isn't working for you. So step one is to get it to return NULL if your COUNT() comes back zero:
SELECT
(0.01 * 2500) / NULLIF( COUNT(broken_cones), 0 )
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
Now you said you wanted that NULL to come back zero? That is where you use ISNULL():
ISNULL(<expression1>, <expression2>)
If the first expression is NULL then return the second expression, so our SQL now becomes:
SELECT
ISNULL(
(0.01 * 2500) / NULLIF( COUNT(broken_cones), 0 ),
0
)
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'

Resources