452

I have this error message:

Msg 8134, Level 16, State 1, Line 1 Divide by zero error encountered.

What is the best way to write SQL code so that I will never see this error message again?

I could do either of the following:

  • Add a where clause so that my divisor is never zero

Or

  • I could add a case statement, so that there is a special treatment for zero.

Is the best way to use a NULLIF clause?

Is there better way, or how can this be enforced?

DineshDB
  • 5,998
  • 7
  • 33
  • 49
Henrik Staun Poulsen
  • 13,154
  • 4
  • 23
  • 26

19 Answers19

781

In order to avoid a "Division by zero" error we have programmed it like this:

Select Case when divisor=0 then null
Else dividend / divisor
End ,,,

But here is a much nicer way of doing it:

Select dividend / NULLIF(divisor, 0) ...

Now the only problem is to remember the NullIf bit, if I use the "/" key.

Henrik Staun Poulsen
  • 13,154
  • 4
  • 23
  • 26
  • 19
    A much nicer Way of doing it "Select dividend / nullif(divisor, 0) ..." breaks if divisor is NULL. – Anderson Dec 01 '14 at 10:51
  • 13
    @Anderson That's not true at all. Are you sure you didn't accidentally use `IsNull` instead of `NullIf`? Try it yourself! `SELECT Value,1/NullIf(Value,0)FROM(VALUES(0),(5.0),(NULL))x(Value);` Unless by "breaks" you mean returns a NULL? You can convert that to whatever you want with `IsNull` or `Coalesce`. – ErikE Jan 26 '17 at 16:54
  • 3
    @ErikE, it is true... try running ... select 1/nullif(null,0) ... you get "The type of the first argument to NULLIF cannot be the NULL constant because the type of the first argument has to be known." Handle this by using "coalesce(FieldName,0)" ... e.g. select 1/nullif(coalesce(null,0),0) – John Joseph May 03 '19 at 16:48
  • 2
    @JohnJoseph I can’t tell if you’re agreeing with me or arguing with me. – ErikE May 03 '19 at 16:52
  • 9
    @JohnJoseph Look closer at the error you got. Yes, `SELECT 1 / NULLIF(NULL, 0)` fails, but it's because `NULLIF()` needs to know the datatype of the first argument. This altered example works fine: `SELECT 1 / NULLIF(CAST(NULL AS INT), 0)`. In real life, you are going to supply a table column to `NULLIF()` rather than a `NULL` constant. Since table columns have known datatypes, this also works fine: `SELECT 1 / NULLIF(SomeNullableColumn, 0) FROM SomeTable`. – MarredCheese Jun 06 '19 at 17:55
  • 2
    How can you possibly get a null divisor where you do not know the datatype unless you literally type "null" as in the example provided by @JohnJoseph? If it comes from a datatable or from a variable, it will have a defined type. Unless I am missing something, it sounds like the only way to break it is to do it intentionally. – RPh_Coder Mar 05 '21 at 16:20
216

In case you want to return zero, in case a zero devision would happen, you can use:

SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable

For every divisor that is zero, you will get a zero in the result set.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tobias Domhan
  • 3,096
  • 1
  • 19
  • 12
  • 9
    Some benchmarks reveal that COALESCE is slightly slower than ISNULL. However, COALESCE is in the standards so is more portable. – Paul Chernoch Jul 12 '12 at 14:29
  • 49
    If someone else doesn't instantly get why this works, NULLIF(d,0) will return NULL if d is 0. In SQL, dividing by NULL returns NULL. The Coalesce replaces the resulting NULL by 0. – GuiSim Apr 16 '14 at 15:59
  • 2
    Note also that it returns 0 when a dividend or divisor are NULL while by standard it would return NULL. – user824276 Mar 07 '15 at 14:07
  • 8
    **PLEASE**! I am shocked that this gets 71 upvotes! Division by zero is UNDEFINED and should **NEVER** return ZERO! I very seldom downvote, but this is really crap! At least please remove the coalesce, then the division returns NULL instead of Zero, that's better. – SQL Police Nov 26 '15 at 17:38
  • 19
    @SQLGeorge While I agree with your argument, please note that there are cases that one cares more what is statistically correct than mathematically correct. In some cases when using statistics functions, 0 or even 1 is an acceptable result when divisor is zero. – Athafoud Feb 03 '16 at 08:26
  • 4
    Doing hacks like this has cost people money, lost people jobs and possibly put lives in danger. This type of answer should never be accepted in any form. If you want a return of 0 (or 1) then you're doing something wrong or asking the wrong question. (Anything / 0) If it's actually 0 then you're doing something wrong and it returns an error as it should. Which means you need to travel up the line and figure out why you're getting 0 or be acceptable with returning null. 0 is never correct in this instance. – James Wilson Mar 07 '16 at 18:22
  • @JamesWilson Totally agree with you. This answer should be banned from Stackoverflow. This is hacking and not thoughtful programming. – SQL Police Jul 06 '16 at 13:03
  • 11
    Can someone explain to me why this is bad? If I'm trying to figure out a percent and the divisor is zero I most certainly want the result to be zero percent. – Todd Sharp Aug 17 '16 at 15:52
  • 12
    I think that @George and @James/ Wilson fundamentally misunderstand the question being asked. There are certainly business applications where returning a "0" is appropriate, even if it is not technically true from a mathematical point of view. – Sean Branchaw Sep 27 '16 at 19:19
  • @SeanBranchaw Please give an example, where 0 as a result would be appropriate. You write "certainly", so you *certainly* have an example ready. – SQL Police Oct 02 '16 at 11:49
  • 4
    @George The particular example that drove me to this page was a proficiency tracker calculating how many values meeting a certain criteria were chosen out of all values chosen. Before the user has entered any data, the initial state for the calculation is 0/0 because no values have been chosen. While that obviously is technically indeterminate, telling the manager that isn't particularly helpful. Returning a null or N/A might be your next choice, but not only is it confusing to understand visually on the dashboard, the application I'm using isn't very flexible. Why would you disagree? – Sean Branchaw Oct 19 '16 at 14:22
  • 1
    @SeanBranchaw I agree, if your user interface cannot handle NULL or N/A, and if you cannot change the UI, then of course you must do something. But that's a **workaround** and not a solution. A **solution** would mean to change the UI such that it can properly handle division by zero. But the OP did not report about such a situation. He only wanted that his query does not stop its execution upon a division by zero. – SQL Police Oct 23 '16 at 16:47
  • 1
    If you give 5 apples to 0 people, how many apples do the people have? The answer is 0, the 0 people have 0 of the 5 apples you have. Therefore the correct answer is 0. Mathematically speaking, a divide by 0 should always return 0. I don't like the arguments saying it is not possible - it's just a rule used by mathematics to allow mathematics to work. Is it possible to give 5 apples to 0 people? Yes it is -but they would cease to exist in the context. – markthewizard1234 Mar 03 '17 at 13:19
  • 1
    @markthewizard1234 A more relevant way to phrase your scenario (so that it clearly involves division by 0 instead of sort of side-stepping it) would be, "5 apples have been divided evenly among 0 people. How many apples does each person have?" There is simply no intelligible answer to that question. Even more absurd, try to imagine splitting an object into 0 pieces. It is clearly impossible, regardless of a lack of motivation "to allow mathematics to work." – MarredCheese Jun 06 '19 at 18:39
  • There is a reason why x/0 or even 0/0 is not 0. A businessman sells a product. The price constantly changes. To see how the price was in average every month he calculates: income/order_count per month. Let's say he did not sell anything on March and we want so calculate the average for the first quarter. Correct: (20, 30, null) => 50/2 = 25. Wrong: (20, 30, 0) => 50/3 = 16,67. (Note that calculating an average in two step this way is not really correct. it's not the average price the businessman got during this quarter. It's more a guess of the average price of the product.) – noox Jun 14 '19 at 14:29
74

This seemed to be the best fix for my situation when trying to address dividing by zero, which does happen in my data.

Suppose you want to calculate the male–female ratios for various school clubs, but you discover that the following query fails and issues a divide-by-zero error when it tries to calculate ratio for the Lord of the Rings Club, which has no women:

SELECT club_id, males, females, males/females AS ratio
  FROM school_clubs;

You can use the function NULLIF to avoid division by zero. NULLIF compares two expressions and returns null if they are equal or the first expression otherwise.

Rewrite the query as:

SELECT club_id, males, females, males/NULLIF(females, 0) AS ratio
  FROM school_clubs;

Any number divided by NULL gives NULL, and no error is generated.

KenD
  • 5,280
  • 7
  • 48
  • 85
frank
  • 749
  • 5
  • 2
  • 7
    Yes indeed, that is **WAY BETTER** than that other answer which has got so many upvotes. In your solution, you have at least a NULL, which indicates that you cannot provide a correct result. But if you convert the result from NULL to Zero, then you simply get wrong and misleading results. – SQL Police Nov 26 '15 at 17:48
  • 10
    By the way, if you want to calculate a male/female ratio, then I suggest to better compare it to the total, like this: `select males/(males+females), females/(males+females)`. This will give you the percentage distribution of males and females in a club, like 31% men, 69% women. – SQL Police Nov 26 '15 at 18:04
53

You can also do this at the beginning of the query:

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

So if you have something like 100/0 it will return NULL. I've only done this for simple queries, so I don't know how it will affect longer/complex ones.

Cosmin
  • 21,216
  • 5
  • 45
  • 60
Taz
  • 531
  • 4
  • 2
  • 1
    Works for me. In my case I have to use divide operation at WHERE clause. I'm sure there is no zero divider, because when I comment WHERE out, there is no zero values at results. But somehow query optimizer do divide by zero while filtering. SET ARITHABORT OFF SET and ANSI_WARNINGS OFF do it work - after 2 days of fighting with divide by zero at WHERE clause. Thx! – huhu78 Sep 27 '12 at 14:17
  • 4
    This "feels" so dirty but I love it! Needed it in a query that does aggregation and use CASE statement was not an option because then I had to add that column to the GROUP BY which totally changed the results. Making the initial query a subselect and then doing a GROUP BY on the outer query also changes the results because there is division involved. – Andrew Steitz Mar 01 '13 at 20:02
  • 1
    OK, so I still like this "solution" but like many of you probably felt, I felt there had to be a "cleaner" way. What if I forgot to re-enable the warnings? Or someone cloaned my code (that never happens, right?) and did not think about the warnings? Anyway, saw other answers about NULLIF(). I knew about NULLIF() but did not realize dividing by NULL returns NULL (I thought it would be an error). So... I went with the following: ISNULL( (SUM(foo) / NULLIF(SUM(bar),0) ), 0) AS Avg – Andrew Steitz Mar 01 '13 at 20:45
  • 2
    I did not know this solution. I'm not sure I like it, but it might be useful to know of, some day. Thank you very much. – Henrik Staun Poulsen Dec 17 '13 at 20:01
  • 1
    This is the easiest solution, but note that it will hurt performance. From https://learn.microsoft.com/en-us/sql/t-sql/statements/set-arithabort-transact-sql#remarks : "Setting ARITHABORT to OFF can negatively impact query optimization leading to performance issues." – mono blaine Oct 31 '17 at 09:14
45

You can at least stop the query from breaking with an error and return NULL if there is a division by zero:

SELECT a / NULLIF(b, 0) FROM t 

However, I would NEVER convert this to Zero with coalesce like it is shown in that other answer which got many upvotes. This is completely wrong in a mathematical sense, and it is even dangerous as your application will likely return wrong and misleading results.

SQL Police
  • 4,127
  • 1
  • 25
  • 54
  • Your concern is valid if it is used in a program. How about if we were to give a report to management with percentages? I think using this: `SUM(Charge_Amount) OVER(Partition BY Charge_Date) AS Total_Charge_Amount,Charge_Amount/ISNULL((NULLIF(SUM(Charge_Amount) OVER(Partition BY Charge_Date),0)),1)*100 AS pct` would be appropriate if used only in the `SELECT` context. `Charge_Amount` in some `Charge_Date` comes `0.00` so the `sum` is also `0` in few instance, and we get an error. Please let me know your thoughts. Thanks – Heike Jan 13 '22 at 16:22
  • @Heike if you want to show a report to management, then show them something like #NA , but DO NOT show them "0%", because this gives the wrong impression that two columna are the same (e.g. if you want to compare actual figures vs. budget figures) – SQL Police Jan 14 '22 at 23:53
36
SELECT Dividend / ISNULL(NULLIF(Divisor,0), 1) AS Result from table

By catching the zero with a nullif(), then the resulting null with an isnull() you can circumvent your divide by zero error.

Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
  • 3
    Due to its length your answer has been recommended for deletion. Note that it is always better to add a small explanation of whatever you are suggesting - even if it seems very simple ;) – Trinimon Sep 16 '15 at 16:42
34

EDIT: I'm getting a lot of downvotes on this recently...so I thought I'd just add a note that this answer was written before the question underwent it's most recent edit, where returning null was highlighted as an option...which seems very acceptable. Some of my answer was addressed to concerns like that of Edwardo, in the comments, who seemed to be advocating returning a 0. This is the case I was railing against.

ANSWER: I think there's an underlying issue here, which is that division by 0 is not legal. It's an indication that something is fundementally wrong. If you're dividing by zero, you're trying to do something that doesn't make sense mathematically, so no numeric answer you can get will be valid. (Use of null in this case is reasonable, as it is not a value that will be used in later mathematical calculations).

So Edwardo asks in the comments "what if the user puts in a 0?", and he advocates that it should be okay to get a 0 in return. If the user puts zero in the amount, and you want 0 returned when they do that, then you should put in code at the business rules level to catch that value and return 0...not have some special case where division by 0 = 0.

That's a subtle difference, but it's important...because the next time someone calls your function and expects it to do the right thing, and it does something funky that isn't mathematically correct, but just handles the particular edge case it's got a good chance of biting someone later. You're not really dividing by 0...you're just returning an bad answer to a bad question.

Imagine I'm coding something, and I screw it up. I should be reading in a radiation measurement scaling value, but in a strange edge case I didn't anticipate, I read in 0. I then drop my value into your function...you return me a 0! Hurray, no radiation! Except it's really there and it's just that I was passing in a bad value...but I have no idea. I want division to throw the error because it's the flag that something is wrong.

Beska
  • 12,445
  • 14
  • 77
  • 112
  • 15
    I disagree. Your business rules should never ever end up doing illegal math. If you end up doing something like this most likely your data model is wrong. Whenever you encounter a divide by 0 you should ponder if the data should had been NULL instead of 0. – Remus Rusanu May 14 '09 at 18:34
  • 33
    I can't believe I was downvoted by someone who asks if I've ever "do any real programming?" because I'm saying to do it right, rather than be lazy. *sigh* – Beska May 14 '09 at 19:12
  • 11
    I'm sorry, I didn't mean to offend you. But the question is perfectly valid in a lot of common LOB applications, and answering it with a "division by 0 is not legal" does not add value IMHO. – Eduardo Molteni May 15 '09 at 17:13
  • what is the best way to ensure that we always consider zero when writing the slash character? – Henrik Staun Poulsen May 18 '09 at 13:49
  • @Eduardo...but that's kind of my point. It's not valid there either. It may happen at times that there is an edge case, or bad user input, or something odd like that. No question about that...and it has to be handled. But my point (which I guess I didn't make very well), is that it should be handled by the business logic up front...capturing this bad/edge/whatever data and flagging it. Because you don't really want to divide by 0, which is impossible; you simply want these extra cases to be handled in an elegant way. That shouldn't be done at the SQL level, I don't think. – Beska May 18 '09 at 14:28
  • henrikstaunpoulsen: I think the best way is to, in general, try to handle it outisde of the SQL. If you're passing in a 0, then you must be in a strange edge case, or have bad data, or something like that...and so there's no point in going to the SQL layer anyway. Just have the code logic return an error code, or null, or whatever it should do. Then you can leave your SQL code as clean as possible, and if you ever *do* see a /0 error coming from there, it will be a good warning that you're not catching a case that you should be...it will be a good thing, rather than a bad thing. – Beska May 18 '09 at 14:36
  • @Remus I hit this often with reporting average selling prices of transactions. Negative transactions are possible and if the +1 sale has a different selling price to the -1, and they are the only 2, the ASP is *mathematically* undefined. However the business defines the result in this case to be 'show a blank for ASP'. I'm not sure if this falls foul of your "Your business rules should never ever end up doing illegal math" rule, but it seems valid enough to me? –  Dec 06 '12 at 11:02
  • 3
    @JackDouglas Right. This is a case where you want the business rules to handle a special case in a special way...but it shouldn't be the underlying math that returns a blank. It should be the business rule. The accepted answer returns a null, which is an okay way to handle it. Throwing an exception would be okay too. Spotting it and handling before it goes to the SQL would be arguably ideal. Providing some kind of function that other things could call that returned a mathematically incorrect value is *not* the way to go, because that special case may not apply for those other callers. – Beska Dec 06 '12 at 15:19
  • @Beska I'd argue that the only "okay way to handle it" is the way that fits you business rules, and given the question, that doesn't necessarily include returning a null, as the OP doesn't provide much context! Of course I agree that failing to understand the issue beyond "there's an error message I need to get rid of" is bonkers :) –  Dec 06 '12 at 15:25
  • 5
    @JackDouglas Yes, that's a good summary, that I agree with. Originally the question seemed to be phrased as "what can I do to just hide this error." Since then, it has evolved. Returning a null, the answer he eventually comes to, seems like one reasonable reponse. (I was strongly advocating not returning a 0, or some other number.) – Beska Mar 14 '13 at 18:08
  • Don't you hate moving targets :) I notice you're signed up to most SE sites but not dba.se: some of your SO database questions would also be on-topic over there, we'd be happy to have you join us ;) –  Mar 14 '13 at 18:13
  • 1
    **Thank you!** Finally someone saying the truth here. A division by zero should never return zero. This is simply wrong and leads to misleading results. Like you say, if you use the zero result for further processing in another function, then you will propagate the error and get more wrong functions. – SQL Police Nov 26 '15 at 18:10
  • 1
    @EduardoMolteni The golden rule of programming is **ALWAYS DELIVER CORRECT RESULTS**. I am almost 30 years in the business, and I can tell you: Whenever you are a little bit sloppy, then it will come back to you like a boomerang. And it will hit you really badly. Therefore, always go the extra mile and try to write every single function as correct and as solid as possible. – SQL Police Nov 26 '15 at 18:14
  • 1
    @Beska I see your sigh. I can also not understand how that other answer which delivers 0 as a "result" ever got 70 upvotes. this shocks me, because programmers should seek to write correct programs, not lazy and sloppy code. – SQL Police Nov 26 '15 at 18:23
  • @George You are totally correct when division is called for. However, there are some cases where division is misapplied. For example, % deviation of x from y. If both x and y are 0 then there is no deviation so the correct result would be 0%. This result is arrived at without division. So, a challenge to anyone who thinks that 0/0=0 in their situation: Explain what it means and see if it makes more sense to leave out the division. If you can't then undefined, NaN or NULL is the right answer. – Tom Blodget Jan 27 '17 at 04:52
  • @TomBlodget If the deviation between 0 and 0 is "0%", what is then the deviation between 800 and 0? 800 $ Sales in 2016, 0 $ Sales in 2015. What is the deviation ? – SQL Police Feb 01 '17 at 00:18
  • The deviation is $800. The % deviation is undefined. My example is like when simplifying algebraic equations; If you divide by a factor that could be 0 under certain conditions, the resulting equation cannot be applied to those conditions. It doesn't mean that there is no solution under those conditions. It doesn't mean that there is one either, which is the **grave mistake that people who are "avoiding division by zero" are making.** – Tom Blodget Feb 01 '17 at 00:44
14

Replacing "divide by zero" with zero is controversial - but it's also not the only option. In some cases replacing with 1 is (reasonably) appropriate. I often find myself using

 ISNULL(Numerator/NULLIF(Divisor,0),1)

when I'm looking at shifts in scores/counts, and want to default to 1 if I don't have data. For example

NewScore = OldScore *  ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1) 

More often than not, I've actually calculated this ratio somewhere else (not least because it can throw some very large adjustment factors for low denominators. In this case I'd normally control for OldSampleScore is greater than a threshold; which then precludes zero. But sometimes the 'hack' is appropriate.

N Mason
  • 326
  • 2
  • 5
5

I wrote a function a while back to handle it for my stored procedures:

print 'Creating safeDivide Stored Proc ...'
go

if exists (select * from dbo.sysobjects where  name = 'safeDivide') drop function safeDivide;
go

create function dbo.safeDivide( @Numerator decimal(38,19), @divisor decimal(39,19))
   returns decimal(38,19)
begin
 -- **************************************************************************
 --  Procedure: safeDivide()
 --     Author: Ron Savage, Central, ex: 1282
 --       Date: 06/22/2004
 --
 --  Description:
 --  This function divides the first argument by the second argument after
 --  checking for NULL or 0 divisors to avoid "divide by zero" errors.
 -- Change History:
 --
 -- Date        Init. Description
 -- 05/14/2009  RS    Updated to handle really freaking big numbers, just in
 --                   case. :-)
 -- 05/14/2009  RS    Updated to handle negative divisors.
 -- **************************************************************************
   declare @p_product    decimal(38,19);

   select @p_product = null;

   if ( @divisor is not null and @divisor <> 0 and @Numerator is not null )
      select @p_product = @Numerator / @divisor;

   return(@p_product)
end
go
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ron Savage
  • 10,923
  • 4
  • 26
  • 35
  • 2
    Hi Ron, Nice solution, except it has a limited data type (4 decimal places) and our @divisors can be negative as well. And how do you enforce it's use? TIA Henrik Staun Poulsen – Henrik Staun Poulsen May 14 '09 at 06:32
  • 1
    I dashed it off pretty quick to handle a specific problem scenario at the time. Single developer app, so enforcement not so difficult except for my memory. :-) – Ron Savage May 14 '09 at 15:02
  • 5
    Despite the print statement, it's not a stored proc, it's a scalar UDF. This will kill you in MS-SQL if it's part of a query. – Mark Sowul May 08 '12 at 19:19
  • 4
    I agreed with Mark Sowul's assertion that the scalar function will cause pain. This is a terrible suggestion in T-SQL, don't do it! Scalar Functions are performance destroyers! In-line table valued function are the only good user functions in SQL Server (possibly with the exception of CLR functions which can perform well). – Davos Feb 26 '14 at 02:52
  • You can replace your whole function with the expression `@numerator / NULLIF(@divisor, 0)`. It does the same and is much more faster. – SQL Police Sep 02 '20 at 12:07
4
  1. Add a CHECK constraint that forces Divisor to be non-zero
  2. Add a validator to the form so that the user cannot enter zero values into this field.
finnw
  • 47,861
  • 24
  • 143
  • 221
4

For update SQLs:

update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)
Vijay Bansal
  • 737
  • 7
  • 10
  • 3
    hi Vijay, Yes, that will work, but... I would be careful about the ISNULL part, where you end up dividing by NULL. I would rather signal to the user that the result is unknown because the divisor is zero. – Henrik Staun Poulsen Sep 17 '15 at 11:49
  • 2
    It's saved me in complicated subQuery, Thanks. – QMaster Feb 20 '17 at 17:16
3

Here is a situation where you can divide by zero. The business rule is that to calculate inventory turns, you take cost of goods sold for a period, annualize it. After you have the annualized number, you divide by the average inventory for the period.

I'm looking at calculating the number of inventory turns that occur in a three month period. I have calculated that I have Cost of Goods sold during the three month period of $1,000. The annual rate of sales is $4,000 ($1,000/3)*12. The beginning inventory is 0. The ending inventory is 0. My average inventory is now 0. I have sales of $4000 per year, and no inventory. This yields an infinite number of turns. This means that all my inventory is being converted and purchased by customers.

This is a business rule of how to calculate inventory turns.

Jimmy
  • 31
  • 1
  • 3
    Yes, you then have an *infinite* number of turns. So in this case, if you have a division by zero, then you should show something like '#INF'. – SQL Police Nov 26 '15 at 18:26
  • 1
    _"The beginning inventory is 0. The ending inventory is 0. My average inventory is now 0."_ Your calculation is an estimate. At some time the inventory is positive or you can't ship/sell anything. If you are unhappy with +∞ as a result, use a better estimation of average inventory. – Tom Blodget Jan 27 '17 at 04:27
3

There is no magic global setting 'turn division by 0 exceptions off'. The operation has to to throw, since the mathematical meaning of x/0 is different from the NULL meaning, so it cannot return NULL. I assume you are taking care of the obvious and your queries have conditions that should eliminate the records with the 0 divisor and never evaluate the division. The usual 'gotcha' is than most developers expect SQL to behave like procedural languages and offer logical operator short-circuit, but it does NOT. I recommend you read this article: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
2
CREATE FUNCTION dbo.Divide(@Numerator Real, @Denominator Real)
RETURNS Real AS
/*
Purpose:      Handle Division by Zero errors
Description:  User Defined Scalar Function
Parameter(s): @Numerator and @Denominator

Test it:

SELECT 'Numerator = 0' Division, dbo.fn_CORP_Divide(0,16) Results
UNION ALL
SELECT 'Denominator = 0', dbo.fn_CORP_Divide(16,0)
UNION ALL
SELECT 'Numerator is NULL', dbo.fn_CORP_Divide(NULL,16)
UNION ALL
SELECT 'Denominator is NULL', dbo.fn_CORP_Divide(16,NULL)
UNION ALL
SELECT 'Numerator & Denominator is NULL', dbo.fn_CORP_Divide(NULL,NULL)
UNION ALL
SELECT 'Numerator & Denominator = 0', dbo.fn_CORP_Divide(0,0)
UNION ALL
SELECT '16 / 4', dbo.fn_CORP_Divide(16,4)
UNION ALL
SELECT '16 / 3', dbo.fn_CORP_Divide(16,3)

*/
BEGIN
    RETURN
        CASE WHEN @Denominator = 0 THEN
            NULL
        ELSE
            @Numerator / @Denominator
        END
END
GO
Gregory Hart
  • 137
  • 1
  • 7
  • I do not like your solution, because using a UDF forces the query to run in a single threaded mode. I like your test setup. I would like to have that in all our UDFs. – Henrik Staun Poulsen Sep 09 '16 at 08:24
  • 1
    For me this solution is perfect and elegant – Payedimaunt Jan 26 '17 at 09:06
  • @Payedimaunt; yes, UDFs lead to very elegant code. But it does not perform well. This is "cursors on sleeping pills". :-) It is also difficult to remember to write dbo.Divide instead of a ordinary "/" – Henrik Staun Poulsen Jan 27 '17 at 09:41
1

Sometimes, 0 might not be appropriate, but sometimes 1 is also not appropriate. Sometimes a jump from 0 to 100,000,000 described as 1 or 100-percent change might also be misleading. 100,000,000 percent might be appropriate in that scenario. It depends on what kind of conclusions you intend to draw based on the percentages or ratios.

For example, a very small-selling item moving from 2-4 sold and a very large-selling item changing from 1,000,000 to 2,000,000 sold might mean very different things to an analyst or to management, but would both come through as 100% or 1 change.

It might be easier to isolate NULL values than to scour over a bunch of 0% or 100% rows mixed with legitimate data. Often, a 0 in the denominator can indicate an error or missing value, and you might not want to just fill in an arbitrary value just to make your dataset look tidy.

CASE
     WHEN [Denominator] = 0
     THEN NULL --or any value or sub case
     ELSE [Numerator]/[Denominator]
END as DivisionProblem
  • 1
    I find that the problem is to remember to do something whenever you want to do a division. If you fail to remember adding CASE or NULLIF, you get a support case in x weeks, on a Monday morning. I hate that. – Henrik Staun Poulsen Nov 28 '18 at 09:08
1

This is how I fixed it:

IIF(ValueA != 0, Total / ValueA, 0)

It can be wrapped in an update:

SET Pct = IIF(ValueA != 0, Total / ValueA, 0)

Or in a select:

SELECT IIF(ValueA != 0, Total / ValueA, 0) AS Pct FROM Tablename;

Thoughts?

Dale K
  • 25,246
  • 15
  • 42
  • 71
CorvetteGuru
  • 167
  • 1
  • 2
  • I and others find that replacing "unknown" with zero is a dangerous fix. I much prefer NULL. But the difficult bit is to remember to add iif or nullif on all divisions! – Henrik Staun Poulsen Jul 16 '19 at 17:15
1

Filter out data in using a where clause so that you don't get 0 values.

nunespascal
  • 17,584
  • 2
  • 43
  • 46
0

You can handle the error appropriately when it propagates back to the calling program (or ignore it if that's what you want). In C# any errors that occur in SQL will throw an exception that I can catch and then handle in my code, just like any other error.

I agree with Beska in that you do not want to hide the error. You may not be dealing with a nuclear reactor but hiding errors in general is bad programming practice. This is one of the reasons most modern programming languages implement structured exception handling to decouple the actual return value with an error / status code. This is especially true when you are doing math. The biggest problem is that you cannot distinguish between a correctly computed 0 being returned or a 0 as the result of an error. Instead any value returned is the computed value and if anything goes wrong an exception is thrown. This will of course differ depending on how you are accessing the database and what language you are using but you should always be able to get an error message that you can deal with.

try
{
    Database.ComputePercentage();
}
catch (SqlException e)
{
    // now you can handle the exception or at least log that the exception was thrown if you choose not to handle it
    // Exception Details: System.Data.SqlClient.SqlException: Divide by zero error encountered.
}
Despertar
  • 21,627
  • 11
  • 81
  • 79
  • 1
    I think we all agree that hiding the error with a 0 is not a solution. What I propose is to write our code such that every "/" is followed by a "NULLIF". This way my report/nuclear reactor is not left alone, but is showing a "NULL" instead of that pesky error Msg 8134. Of cause, if I need a different process when divisor is 0, then Beska and I agree. We need to code that. It is just difficult to do every time you write a "/". "/ NULLIF" is not impossible to do every time. – Henrik Staun Poulsen Jan 27 '17 at 09:56
0

Use NULLIF(exp,0) but in this way - NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0) breaks if exp is null but NULLIF(ISNULL(exp,0),0) will not break

Henrik Staun Poulsen
  • 13,154
  • 4
  • 23
  • 26