MSSQL: When result of calculation is negative, return 0 - sql

I have 2 colums:
WarehouseStock (value 10)
WarehouseReserved (value 20)
I want to get a zero when SELECT (WarehouseStock - WarehouseReserved) as Warehousetotal;
Is there a function or something that will convert negative numbers into 0?

One solution is to use "CASE" statement:
SELECT
(CASE
WHEN WarehouseStock - WarehouseReserved < 0 THEN 0
ELSE WarehouseStock - WarehouseReserved
END) as Warehousetotal
FROM [your_table]
Another could be using VALUE and MAX functions:
SELECT
(SELECT MAX(v)
FROM (VALUES (0), (WarehouseStock - WarehouseReserved)) AS VALUE(v)) as Warehousetotal
FROM [your_table]

The case expression is what you use if you want your intent to be obvious. If you want inscrutable code, you could use:
select (WarehouseStock - WarehouseReserved) * (sign(WarehouseStock - WarehouseReserved) + 1) / 2

Related

bigQuery throws "division by zero: 0 / 0" error

I am doing the percentage by the frequency of column value using bigquery. However, some of the value might be zero, so the query will return error for sure
(division by zero: 0 / 0)
How to apply kind of IFERROR(x/y,null) in this case? so the query will bounce null value as the result instead of error?
SELECT
User_ID,
ROUND(SUM(CASE WHEN Name LIKE '%MIKE%' THEN 1 ELSE 0 END) / COUNT(Name) * 100 ,1) AS Percentage_of_MIKE,
FROM
table
GROUP BY
User_ID
TRIED:
ROUND(SAFE_DIVIDE(SUM(CASE WHEN Name LIKE '%MIKE%' THEN 1 ELSE 0 END) / COUNT(Name) * 100 ,1)) AS Percentage_of_MIKE,
You can just use SAFE_DIVIDE function in such cases
Something like in below example
ROUND(SAFE_DIVIDE(SUM(CASE WHEN Name LIKE '%MIKE%' THEN 1 ELSE 0 END), COUNT(Name) * 100) ,1) AS Percentage_of_MIKE
I tend to use NULLIF() for this purpose, because I like using the division operator for division:
SELECT User_ID,
ROUND(COUNTIF(Name LIKE '%MIKE%') * 100 / NULLIF(COUNT(Name), 0), 1) AS Percentage_of_MIKE
FROM table
GROUP BY User_ID;
This error indicates that you have User_IDs whose all Names are NULL. So the denominator of your division is 0 (COUNT(Name) counts non-null values of Name), and you get the division by 0 error.
A simple way to avoid this is to use AVG():
ROUND(AVG(CASE
WHEN Name LIKE '%MIKE%' THEN 1.0
WHEN Name IS NOT NULL THEN 0
END) * 100, 1) AS Percentage_of_MIKE

Why Sum in database query giving NULL

Suppose I have a table named "Expense" and it has some month-wise expense which may contain some "NULL", now I want to check the yearly expenses in total with this below query:-
Select Sum(January) + Sum (February) ..... (I skipped the rest 10 months)
from Expense
This gives result as "NULL"
How can I avoid this situation? I think there are more convenient way to check the yearly sum
All arithmetic or logical operations involving NULL yield NULL. For example:
SELECT 1 + NULL -- NULL
You must convert NULL to zeros before you can + them:
SELECT
COALESCE(SUM(January), 0) +
COALESCE(SUM(February) , 0) +
...
It is also possible to add the columns first and then calculate the sum:
SELECT SUM(
COALESCE(January, 0) +
COALESCE(February, 0) +
)
Be advised that (i) SUM skips NULL values (ii) returns NULL instead of 0 if all values are NULL:
SELECT SUM(a) FROM (VALUES
(1),
(2),
(NULL)
) AS v(a) -- returns 3 instead of NULL
It will return NULL if all values encountered were NULL:
SELECT SUM(a) FROM (VALUES
(CAST(NULL AS INT)),
(NULL),
(NULL)
) AS v(a) -- returns NULL instead of 0
use coalesce function to convert null to 0 then use sum
Select Sum(coalesce(January,0)) + Sum (coalesce(February,0)) ..... (I skipped the rest 10 months)
from Expense
Just use coalesce [ with 0 as the second argument ] to replace nulls for all month columns, otherwise you can not get true results from aggregation of numeric values :
select sum(coalesce(January,0)+coalesce(February,0) ... )
from Expense
That because you have NULL values, you can use Case, Coalesce or IIF:
Select SUM(IIF(Col IS NULL, 0, Col))
Select SUM(CASE WHEN Col IS NULL THEN 0 ELSE Col END)
Select COALESCE(Sum(Col), 0)
Any arithmetic function will return null if there is at least one null value in the given column. That's why you should use functions like coalesce or isNull (MSSQL), NVL (Oracle).
You can use ISNULL(SUM(January),0).
Because null + value is always null and in your sample some months sums are null, you can avoid this by adding ISNULL
Select isnull(Sum(January),0) +
isnull(Sum(February),0)
--..... (I skipped the rest 10 months)
from Expense
Alternatively you can use below way:
Select Sum(
isnull(January,0) +
isnull(February,0)
)
--..... (I skipped the rest 10 months)
from Expense

Select NULL if a calculated column value is negative in SQL

I have the following piece of sql below. The second line (commented out) contains my addition of a check to see if the calculation returns a negative value in which case it should select NULL. This case is within a block of multiple other case statements. Since my approach means running the same calculation twice is there a better alternative or more efficient method to selecting NULL if the value of this calculated column is negative, rather than doing two similar calculations?
Thanks
CASE
WHEN M.ALPHA = 'B' OR T.CT IN (0.001, 0.002) THEN NULL
-- WHEN ((M.VAL / NULLIF (M.VAL2, 0)) / (NULLIF (T.VAL, 0) / T.VAL2)) < 0 THEN NULL
ELSE (M.VAL / NULLIF (M.VAL2, 0)) / (NULLIF (T.VAL, 0) / T.VAL2)
END As WORLD
You could move the calculation to a subquery. For example:
select case
when CalculatedColumn > 42 then 'Hot'
when CalculatedColumn < 42 then 'Cold'
else 'Answer'
end as Description
from (
select 2 * col1 + 3 as CalculatedColumn
from YourTable
) SubQuery
Sometimes it's clearer to define the subquery in a with clause:
; with SubQuery as
(
select 2 * col1 + 3 as CalculatedColumn
from YourTable
) SubQuery
select case
when CalculatedColumn > 42 then 'Hot'
when CalculatedColumn < 42 then 'Cold'
else 'Answer'
end as Description
from SubQuery

Simplifying CASE WHEN SQL statement

Im trying to improve the following CASE statement to calculate the difference only once. I do it to avoid negative numbers:
SELECT (CASE WHEN ((SELECT 100 - (SELECT COUNT(CustomerId) FROM Customers)) > 0)
THEN (SELECT 100 - (SELECT COUNT(CustomerId) FROM Customers))
ELSE (0)
END)
This not only looks stupid, but also is not thread-safe. I tried the following, but I get an error message "Invalid column name 'diff'."
SELECT (CASE WHEN ((SELECT 100 - (SELECT COUNT(CustomerId) FROM Customers) as diff) > 0)
THEN (diff)
ELSE (0)
END)
How can this be simplified? Is there an in-built SQL function that already does this job?
EDIT: Sorry, forgot to mention that the select statement is inside of a view declaration, so I cant declare variables.
If I follow your logic right, this should do it:
SELECT COALESCE ((SELECT 100 - COUNT(CustomerID)
FROM Customers
HAVING COUNT(CustomerID) < 100), 0)
Slightly more graceful.
If there was a row-based MAX() function, we could use that too. Of course you could write one easily enough.
DECLARE #diff int
SELECT #diff = 100 - COUNT(*) FROM Customers
SELECT CASE WHEN #diff > 0 THEN diff
ELSE 0
END as Diff
You could use a variable to start the result of the query so you don't have to execute it twice. For example:
DECLARE #CustomerCount INT
SELECT #CustomerCount = COUNT(CustomerId) FROM Customers
SELECT CASE WHEN (100 - #CustomerCount > 0)
THEN 100 - #CustomerCount
ELSE (0)
END
You are right, you only want to count once and you want to do it so it clearly shows what your logic is.
SELECT CASE WHEN cnt > 100 THEN 0
ELSE 100 - cnt END AS diff
FROM (SELECT COUNT(1) AS cnt
FROM Customers) AS CustomerCnt

How to solve Divide by Zero exception in SQL stored procedure?

When I am executing my stored procedure it is throwing Divide By zero Exception. Because In my database there is no data for some months. How can I solve this?
Below is the my query.
#diffNSVY FLOAT = 0 --I have declared #diffNSVY as optional parameter.
SET #diffNSVY = CASE
WHEN (select top 1 NSV from #temp where rn = 1) < 0 THEN 0
ELSE (((select top 1 NSV from #temp where descrn = 1) - (select top 1 NSV from #temp where rn = 1))*1.0)/(select top 1 NSV from #temp where rn = 1)
END
I am inserting a result set into #temp table. NSV is a column name. rn is rownumber.descrn is also row number in decreasing order.
How can I modify my query?
Please reply.
Regards,
NSJ
First, I would rebuild your script so I didn't need to repeat complex expressions (the subselects, to be precise) more than once.
If possible, use SELECT instead of SET, like this:
SELECT #diffNSVY = CASE
WHEN rn.NSV < 0 THEN 0
ELSE (descrn.NSV - rn.NSV) * 1.0 / rn.NSV /* extra '()' are removed as unneeded */
FROM
(select top 1 NSV from #temp where rn = 1) AS rn,
(select top 1 NSV from #temp where descrn = 1) AS descrn
Next, ask yourself, what the result should be in case of division by zero. Should it be zero as well? Then the next optimisation step would be simply this:
SELECT #diffNSVY = CASE
WHEN rn.NSV <= 0 THEN 0 /* changed '<' to '<=' to account for division by zero */
ELSE (descrn.NSV - rn.NSV) * 1.0 / rn.NSV
FROM
(select top 1 NSV from #temp where rn = 1) AS rn,
(select top 1 NSV from #temp where descrn = 1) AS descrn
But if you wish the result to become undefined (NULL) so you process it later, here's how you can achieve this:
SELECT #diffNSVY = CASE
WHEN rn.NSV < 0 THEN 0
ELSE (descrn.NSV - rn.NSV) * 1.0 / CASE rn.NSV WHEN 0 THEN NULL ELSE rn.NSV END
FROM
(select top 1 NSV from #temp where rn = 1) AS rn,
(select top 1 NSV from #temp where descrn = 1) AS descrn
Generally, I find this pattern useful when I need to secure myself from divide-by-zero errors. I often use it like this:
...ISNULL(some_expression / CASE #Value WHEN 0 THEN NULL ELSE #Value END, 0)...
Sometimes I use it without ISNULL in which case I process the possible NULL result later using some more sophisticated logic.
EDIT: Oh, and one more thing: with SELECT you can have several assignments at once, like this:
SELECT
#Var1 = expression1,
#Var2 = expression2,
...
Could this possibly help you to simplify your query too?
That expression of yours is very unclear and hard to understand, and you're selecting the same value several times which is totally unnecessary - so my recommendation would be:
try to first determine all the bits and pieces that might go into your calcuation - put the results of those select top 1 .... queries into variables
then check before you divide by zero, and if you divisor would be zero, you need to think of another solution / another value to use instead...
Your problem is this: you're currently only checking for your one value being < 0 and then your return 0 - otherwise (including when that value is = 0) you return an expression which is a division by exactly that value.... You need to add one more special case: if that value is = 0, you cannot use your expression since that results in the divide by zero exception - you need to return some other value in this case!
So your code would be something like:
DECLARE #diffNSVY FLOAT = 0 --I have declared #diffNSVY as optional parameter.
DECLARE #RNValue INT
SET #RNValue = (SELECT TOP 1 NSV FROM #temp WHERE rn = 1)
DECLARE #DescRNValue INT
SET #DescRNValue = (SELECT TOP 1 NSV FROM #temp WHERE descrn = 1)
SET #diffNSVY =
CASE
WHEN #RNValue < 0 THEN 0
WHEN #RNValue = 0 THEN ....... <-- you need to define a value here! CAnnot divide by #RNValue if it's ZERO !
ELSE ((#DescRNValue - #RNValue) * 1.0) / #RNValue
END
Maybe you need a '<=0' not '<0' in your if clause? You should also probably make sure there's data in your temp table that meets the rn = 1 criteria, otherwise the selection will return null. If all else fails Sql2005 has try catch blocks, so you can catch the divide by zero exception.