Divide Zero error when calculating Sales % in SQL - sql

I keep getting the following error message:
Msg 8134, Level 16, State 1, Line 1 Divide by zero error encountered.
I've seen some posts about using IFNULL which I have tried.
For example:
,Case when a.DiscountReasonCode = 'RL' then IFNULL(((a.ORIGINALRETAIL-a.RetOne) / (a.ORIGINALRETAIL) * 100),0) end as [PctSB]
But this returns the following error:
'IFNULL' is not a recognized built-in function name.
What I'm doing is trying to calculate the proper sales percent and then find and compare it to what is already in the table to find errors or missing Sales %'s.
I'm not sure where I'm going wrong but any help would be greatly appreciated.
SELECT
a.packnum
,a.description
,a.CatID
,a.PctSavings
,Case when a.DiscountReasonCode = 'RL' then (a.ORIGINALRETAIL-a.RetOne) / (a.ORIGINALRETAIL) * 100
end as [PctSB]
FROM PIC704Current a Join CatalogInfo b ON (a.CatID = b.Catalog) and (a.Year = b.MailYear)
WHERE
b.MediaId in('CAT Catalog','SCAT Sale Catalog','SSTF Sale Statement Stuff','STUF Statement
Stuffer','PKG Package Insert','SPKG Sale Pkg Insert')
and a.DiscountReasonCode = 'RL'
and a.year >='2020'
and (Case when a.PctSavings <> (a.ORIGINALRETAIL-a.RetOne)/a.ORIGINALRETAIL*100 then 'False' else
'True' END) = 'False'
Thanks

Wrapping a divide in a null test will not get rid of the divide by 0 error. Instead, you need to check whether the value being divided by is 0 and return a different value instead. You can do this using IIF:
IIF(a.ORIGINALRETAIL = 0, 0, 100.0 * (a.ORIGINALRETAIL-a.RetOne) / a.ORIGINALRETAIL)

You can use coalesce or isnull . MySQL uses ifnull
case
when a.DiscountReasonCode = 'RL' then (a.ORIGINALRETAIL-a.RetOne) /
NULLIF(a.ORIGINALRETAIL, 0) * 100
end as [PctSB]

Msg 8134, Level 16, State 1, Line 1 Divide by zero error encountered.
This error suggests that your denominator is 0 so you can change it to NULL using a case expression.
'IFNULL' is not a recognized built-in function name.
In SQL Server , you can use ISNULL or Coalesce
CASE WHEN a.DiscountReasonCode = 'RL'
THEN ISNULL(((a.ORIGINALRETAIL-a.RetOne)/(CASE WHEN a.ORIGINALRETAIL = 0
THEN NULL
ELSE a.ORIGINALRETAIL
END) * 100),0)
END AS [PctSB]

I think the posts you've seen mentioning IFNULL might actually have been mentioning NULLIF, you just remembered it the wrong way round. One of the typical tricks to avoid a DIV/0 error in sqlserver:
some_number / NULLIF(other_number_maybe_zero, 0)
If the divisor is 0, it is converted to null, meaning the result is null rather than an error
In standard SQL you can do it too:
some_number / CASE WHEN other_number_maybe_zero = 0 THEN NULL ELSE other_number_maybe_zero END
It's just a bit more wordy

The logic that you want is provided by NULLIF():
(a.ORIGINALRETAIL - a.RetOne) * 100.0 / NULLIF(a.ORIGINALRETAIL, 0)) * 100) as [PctSB]
Using the standard SQL function NULLIF() is the simplest way to do what you want, and I recommend that you write the logic this way. It replaces any 0 value with NULL -- thereby avoiding the divide-by-zero.

Related

Where to use NULLIF inside of a CASE WHEN?

I'm getting a divide by zero error when performing the below code. I have tried a NULLIF after the ELSE, but I am encountering the same error message:
SELECT
country_IBS
, Sku
, LTM_Sales_USD
, PTM_Sales_USD
, LTM_Cost_USD
, (CASE WHEN -([LTM_Sales_USD]-[PTM_Sales_USD])/([LTM_Sales_USD]+[PTM_Sales_USD]) IS NULL
THEN 0
ELSE -([LTM_Sales_USD]-[PTM_Sales_USD])/([LTM_Sales_USD]+[PTM_Sales_USD]) END +1)/2 AS "Sales Growth % (scaled)"
FROM #tempSalesChange
Wrap the divider in the NULLIF:
{Expression 1} / NULLIF({Expression 2},0)
That will mean that if the divider is 0 then NULL will be returned, and the divide by zero error will be avoided.
In your case, that's ([LTM_Sales_USD]+[PTM_Sales_USD]).
If suspect that you want:
(1 - ([LTM_Sales_USD] - [PTM_Sales_USD]) / NULLIF([LTM_Sales_USD] + [PTM_Sales_USD], 0)
) / 2 AS [Sales Growth % (scaled)]
That is: the divisor should be wrap in NULLIF(..., 0). This avoids the division by zero error - in that case, the whole computation returns NULL instead.

how to use is null with case statements

Hi can any one say me how to write query in sql server management studio for statement =>if(isnull("GROSS_SF")=1 or "GROSS_SF"=0,0,"GROSS_SALES"/"GROSS_SF")
by using case statement :
CASE
WHEN ("GROSS_SF"=1 or "GROSS_SF"=0) isnull then 0
else "GROSS_SALES"/"GROSS_SF"
end/* i am getting error if i write it like this */
thanks in advance
If you want to avoid a divide by zero, while at the same time also handling NULL, then you may try the following logic:
CASE WHEN COALESCE(GROSS_SF, 0) = 0
THEN 0
ELSE GROSS_SALES / GROSS_SF END AS output
To be clear, the above logic would return a zero value should either the GROSS_SF be zero or NULL.
I think what are you looking for is IFNULL, look here https://www.w3schools.com/sql/sql_isnull.asp
You are close. You can do:
(CASE WHEN "GROSS_SF" IS NULL OR "GROSS_SF" = 0 THEN 0
ELSE "GROSS_SALES" / "GROSS_SF"
END)
In SQL, divide by zero errors are often avoided using NULLIF():
"GROSS_SALES" / NULLIF("GROSS_SF", 0)
However, that returns NULL rather than 0. If you really want zero:
COALESCE("GROSS_SALES" / NULLIF("GROSS_SF", 0), 0)
This is a bit shorter to write and almost equivalent to your version (this returns 0 if "GROSS_SALES" is NULL, which your version does not).
You can use NULLIF() to prevent arithmetic divide by zero errors :
select . . . ,
GROSS_SALES / nullif(GROSS_SF, 0)
from table t;
However, this would return null instead of error, if you want to display 0 instead then you need to use isnull() (MS SQL Specific) or coalesce() (SQL Standard).
So, you can express it :
select . . . ,
isnull(GROSS_SALES / nullif(GROSS_SF, 0), 0)
from table t;

Adding text string to CASE Statement

I am using the following SQL CASE:
SELECT
BomMast.BomStockCode
, BomMast.BomDescription
, CASE
WHEN StkItem.AveUCst <= 0 THEN 'ERROR'
WHEN StkItem.AveUCst > 0 THEN (StkItem.AveUCst * BomComp.ProductionQty)
END AS TotalCost
FROM BomComp
INNER JOIN BomMast
ON BomMast.BomID = BomComp.BomMasterKey
INNER JOIN StkItem
ON StkItem.StockLink = BomComp.ComponentStockLink
But I get the following message:
Msg 8114, Level 16, State 5, Line 2
Error converting data type varchar to float.
Am I not allowed to add test within the CASE statement?
Thank you!
Change your query to:
SELECT BomMast.BomStockCode
,BomMast.BomDescription
,CASE
WHEN StkItem.AveUCst <= 0
THEN 'ERROR'
WHEN StkItem.AveUCst > 0
THEN CAST((StkItem.AveUCst * BomComp.ProductionQty) AS NVARCHAR(MAX))
END AS TotalCost
FROM BomComp
INNER JOIN BomMast ON BomMast.BomID = BomComp.BomMasterKey
INNER JOIN StkItem ON StkItem.StockLink = BomComp.ComponentStockLink
The datatypes of the values you want to show in either branches of your CASE statements need to be the same in order to work.
Edit:
After #underscore_d's suggestion, I also consider that it would be a far better option to display NULL instead of the message ERROR and then handle this NULL value in the application level.
Hence, your case statement will change to:
CASE
WHEN StkItem.AveUCst <= 0
THEN NULL
WHEN StkItem.AveUCst > 0
THEN (StkItem.AveUCst * BomComp.ProductionQty)
END AS TotalCost
Yes, text can be used as the result a case statement, as can any datatype, but each case must return the same type, as the results column must have one type only.
Your [TotalCost] column has conflicting data types. [StkItem.AveUCst] is a float and the literal value of 'ERROR' is a varchar. If you are intending to retain the benefits of number-based value in your results column, consider replacing 'ERROR' with the SQL keyword NULL.
Your column TotalCost (neither any other column) can be a type-mixture. In first case it would be a varchar, in second case it would be floator something like that. THAT IS NOT POSSIBLE.

divisor is equal to zero in sql need to add some sort of try catch

I have this query that i get errors when data gets loaded to this table and the divisor is zero which is the b. living_units column. I want to know if I can do some sort of try catch or if its zero to show null instead of failing or something that will help not error out?
SELECT a.SERVICE_TYPE_GRP,
a.HSIA_TYPE,
a.STAT_DYN_IND,
a.VIDEO_IND,
a.VOICE_IND,
a.CUST_CNT,
b.LIVING_UNIT_CNT,
a.DT_MODIFIED,
a.CUST_CNT / b.LIVING_UNIT_CNT AS ALLRGN_TK_RT_PCT
FROM ( SELECT lp.SERVICE_TYPE_GRP SERVICE_TYPE_GRP,
lp.HSIA_TYPE HSIA_TYPE,
lp.STAT_DYN_IND STAT_DYN_IND,
lp.VIDEO_IND VIDEO_IND,
lp.VOICE_IND VOICE_IND,
lp.DT_MODIFIED DT_MODIFIED,
SUM (lp.CUST_CNT) CUST_CNT
FROM RPT_SUBSCR_REGION_DTL lp
GROUP BY SERVICE_TYPE_GRP,
HSIA_TYPE,
STAT_DYN_IND,
VIDEO_IND,
VOICE_IND,
DT_MODIFIED) a,
( SELECT DT_MODIFIED, SUM (LIVING_UNIT_CNT) LIVING_UNIT_CNT
FROM RPT_REGION_CUST_DTL
WHERE dt_modified = (SELECT dt_modified
FROM ls_dt_modified
WHERE NAME = 'RPT_REGION_CUST_DTL')
GROUP BY DT_MODIFIED) b
WHERE a.DT_MODIFIED = b.DT_MODIFIED;
The error i get is ORA-01476: divisor is equal to zero.
If you have a complex divisor expression and a NULL result is acceptable, there is an alternative that does not require repeating the divisor expression:
a.CUST_CNT / nullif(b.LIVING_UNIT_CNT, 0)
The NULLIF function returns NULL if the first parameter is equal to the second parameter. In this case it returns NULL if b.LIVING_UNIT_CNT is equal to zero. And anything divided by NULL also becomes NULL. It's a bit of a "trick" maybe, but it saves the expression repetition of the CASE statement.
Try a case statement:
case
when b.LIVING_UNIT_CNT = 0 -- the divisor
then 0 -- a default value
else a.CUST_CNT / b.LIVING_UNIT_CNT
end
ALLRGN_TK_RT_PCT

Specify order of (T)SQL execution

I have seen similar questions asked elsewhere on this site, but more in the context of optimization.
I am having an issue with the order of execution of the conditions in a WHERE clause. I have a field which stores codes, most of which are numeric but some of which contain non-numeric characters. I need to do some operations on the numeric codes which will cause errors if attempted on non-numeric strings. I am trying to do something like
WHERE isnumeric(code) = 1
AND CAST(code AS integer) % 2 = 1
Is there any way to make sure that the isnumeric() executes first? If it doesn't, I get an error...
Thanks in advance!
The only place order of evaluation is guaranteed is CASE
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS integer) % 2
END = 1
Also just because it passes the isnumeric test doesn't guarantee that it will successfully cast to an integer.
SELECT ISNUMERIC('$') /*Returns 1*/
SELECT CAST('$' AS INTEGER) /*Fails*/
Depending upon your needs you may find these alternatives preferable.
Why not simply do it using LIKE?:
Where Code Not Like '%[^0-9]%'
Btw, either using my solution or using IsNumeric, there are some edge cases which might lead one to using a UDF such as 1,234,567 where IsNumeric will return 1 but Cast will throw an exception.
Why not use a CASE statement to say something like:
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS int) % 2 = 1
ELSE /* What ever else if not numeric */ END
You could do it in a case statement in the select clause, then limit by the value in an outer select
select * from (
select
case when isNum = 1 then CAST(code AS integer) % 2 else 0 end as castVal
from (
select
Case when isnumeric(code) = 1 then 1 else 0 end as isNum
from table) t
) t2
where castval = 1