Select NULL if a calculated column value is negative in SQL - 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

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

MSSQL: When result of calculation is negative, return 0

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

Only one expression can be specified in the select list w

I am having problem in part of my code anyway to do this
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS. The update part is working but how to use insert into to calculate if a condition is not meant it will insert.
IF
/* CHECKLIST TO UPDATE*/
(NOT EXISTS
(SELECT *
FROM ENERGY.D_ENERGY_REFERENCE D_ENERGY_REFERENCE
,ENERGY.D_CHECK_LIST D_CHECK_LIST
WHERE D_ENERGY_REFERENCE.ID = D_CHECK_LIST.ID
AND D_ENERGY_REFERENCE.REFERENCE = 19051
)
)
BEGIN
INSERT INTO DB.D_ENERGY_REFERENCE(ID, REFERENCE_NO, REFERENCE,VALUE_INTEGER)
(SELECT ID,
(SELECT ISNULL(MAX(REFERENCE_NO), 0) + 1 FROM DB.D_ENERGY_REFERENCE),
19051, (SELECT D_CHECK_LIST.ID,
CASE
WHEN CAST(COUNT(CASE WHEN D_CHECK_LIST.EVALUATION NOT IN (0,1) THEN EVALUATION ELSE NULL END) AS FLOAT) = 0 THEN NULL
ELSE
(
CAST(COUNT(CASE WHEN D_CHECK_LIST.EVALUATION IN (2, 3, 50001, 50003, 50004, 50005, 50006, 50020, 50027, 50028) THEN EVALUATION ELSE NULL END) AS FLOAT)
/
CAST(COUNT(CASE WHEN D_CHECK_LIST.EVALUATION NOT IN (0,1) THEN EVALUATION ELSE NULL END) AS FLOAT)
) * 100
END FROM DB.D_CHECK_LIST
GROUP BY D_CHECK_LIST.ID)
FROM DB.D_ENERGY_REFERENCE D_ENERGY_REFERENCE
WHERE D_ENERGY_REFERENCE.ID = ID AND D_ENERGY_REFERENCE.REFERENCE = 19051
GROUP BY D_ENERGY_REFERENCE.ID
)
END
Can you please check this following part in the sub query of your script-
.......
19051,
(
SELECT
D_CHECK_LIST.ID, -- This is the column 1
CASE
WHEN -- Here you are generating column 2 in the sub query
......
)
Here you are selecting 2 column - one is "D_CHECK_LIST.ID" and other one is generation through CASE WHEN statement. I think you should SELECT any 1 column from those 2 column. If both are required, you can use separate Sub query for that.
The ERROR code "Only one expression can be specified in the select list when the subquery is not introduced with EXISTS" is self explanatory that you can not implement a Sub Query with more than 1 column selected unless the Sub Query is using inside EXISTS method.

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

SQL Server column divide with column with null value

I wanted my outcome to be something like below, the initial null value in either hit1 or hit0n1 become 0, and when hit1 divide by 0 then the percentage will become 0%.
Select CONVERT(varchar(10),ISNULL(a6.hit1*100,0)/NULLIF(a7.hit0n1, 0))+ '%'
.........
left join
(select category,hit,COUNT(hit) as hit1 from(Select Category,UpdateTime,due,
Case When UpdateTime < Due Then 1 Else 0 End As hit From FeedbackDetail Where StatusName ='Closed' and FeedbackDatetime>=DATEADD(day,-31, GETDATE())) a where hit = 1
Group by category, hit) a6 On a1.Category = a6.Category
left join
(select category,COUNT(hit) as hit0n1 from ( Select Category,UpdateTime,due,
Case when UpdateTime < Due then 1 else 0 end as hit From FeedbackDetail Where StatusName ='Closed' and FeedbackDatetime>=DATEADD(day,-31, GETDATE()) ) a
Group by category)
a7 on a1.Category = a7.Category
Expected Outcome
hit1 hit0n1 percentage
_____ _______ ____________
5 0 0%
0 0 0%
3 10 33.3%
but my code display this instead :
hit1 hit0n1 percentage
_____ _______ ____________
5 null NULL
6 6 100%
3 10 33.3%
there is null value in hit0n1, i want to elimate the null value and at the same time i want to avoid 0 number.
select hit1 = a6.hit1,
hit0n1 = isnull(a7.hit0n1, a6.hit1),
percentage = CONVERT(varchar(10),
ISNULL(a6.hit1 * 100.0,0) / ISNULL(a7.hit0n1, a6.hit1) ) + '%'
if you wanted the result in 3 decimal places,
use convert() to convert the calculation result to decimal(10,3) before convert to varchar(10)
percentage = CONVERT(varchar(10),
CONVERT(decimal(10,3),
ISNULL(a6.hit1 * 100.0,0) / ISNULL(a7.hit0n1, a6.hit1) )
) + '%'
EDIT : change to handle hit0n1 = 0
select
hit1,
hit0n1 = isnull(hit0n1, hit1),
percentage = CONVERT(varchar(10),
CONVERT(decimal(10,3),
ISNULL(ISNULL(hit1 * 100.0, 0) / NULLIF(ISNULL(hit0n1, hit1), 0), 0) )
) + '%'
EDIT : explanation on the percentage calculation
ISNULL(hit0n1, hit1) returns first non null value. If hit0n1 is null then it will return hit1 value
if hit0n1 is 0, that division will be error cause it is divide by 0. To handle it NULLIF( <expression> , 0) is use to return NULL value if <expression> is 0. And the result of the division will be NULL value
the final ISNULL() is to change the NULL value to 0
When you want to filter something, you can use WHERE or HAVING. As HAVING requires aggregation, I think it will be clearer to use WHERE. As, we do not want to have NULL values in the result, we can use:
WHERE a6.hit1 IS NOT NULL AND NULLIF(a7.hit0n1, 0) IS NOT NULL
We check for 0 only the a7.hit0n1 as 0 divided on something is valid statement.
Also, if you are working with huge amount of data, you can add filtering indexes on your tables, excluding the NULL values.
In place of NULL what do you want to replace then?
If you want to use the same value as first column then may be you can go with ISNULL(hit0n1,hit1)
NULLIF returns NULL if both expressions are equal otherwise it returns expression one which is also a NULL in your case.
Try the following query-:
Select hit1,
case when isnull(hit0n1,hit1)!=0 then isnull(hit0n1,hit1) end as [hit0n1],
cast(round((hit1/(case when isnull(hit0n1,hit1)!=0 then isnull(hit0n1,hit1) end)*100),2) as varchar(100))+'%'
from [YOUR_TABLE_NAME]
SQL Server 2014