Avoid divide by zero with an IF SQL - sql

I have this query:
SELECT sum((a.cant*b.cost)/b.cant) AS sum1
FROM
(SELECT partNo,cant
FROM table1 WHERE id=1) AS a,
(SELECT color,cost,cant
FROM table2 WHERE color in
(SELECT partNo FROM table1 WHERE id=1)) AS b
WHERE a.partNo=b.color
The problem is that sometimes b.cant will return 0, and when I do this part: SELECT sum((a.cant*b.cost)/b.cant) AS sum1 it will return a divide by zero error message, I was thinking on doing something like this:
SELECT IF (b.cant=0,0,sum((a.cant*b.cost)/b.cant)) AS sum1
Meaning that if b.cant equals zero then I want to return a 0 as final result and stop doing the division, but if it is not equal 0 then I will do the division, in my head this was the solution but is giving me an error.
Is there any other way to avoid doing the division and just return 0 if the divisor is 0?

You should fix your JOIN syntax and simplify the query:
SELECT SUM(a.cant*b.cost) / NULLIF(b.cant, 0) AS sum1
FROM table1 a JOIN
table2 b
ON a.partNo = b.color AND
a.id = b.id
WHERE a.id = 1;
It is rather suspicious that the join condition is between "partno" and "color", but that is how you have phrased it in your query. I wouldn't be surprised if that were an error.
The answer to your specific question is NULLIF(). But you should also learn how to write clearer SQL code. In particular, never use commas in the FROM clause. Learn to use proper, explicit, standard, readable JOIN syntax.

You should use JOIN instead of selecting columns using select statement. to avoid 0 you should use case expression as following
select
sum((a.cant*b.cost)/b.can) as sum1
from
(
select
a.partNo,
a.cant,
b.color,
b.cost,
(case when b.cant = 0 then 1 else b.cant) as b.cant
from table1 a
join table2 b
on a.partNo = b.color
where a.id = 1
) subq

You can use NULLIF() to return NULL when b.cant is 0:
SUM(a.cant * b.cost / NULLIF(b.cant, 0))
If there is a case that the final sum returns NULL but you want to see 0 then also use COALESCE():
COALESCE(SUM(a.cant * b.cost / NULLIF(b.cant, 0)), 0)

In terms of business, you can judge that b.cant equals zero, if b.cant equals zero then you can do the default.

Related

How to replace SELECTS in an Aggregate Function SUM()

How do I create a workaround for a SELECT statement in a SUM-Function
I am currently migrating my Sybase Database towards MsSQL.
One of my Views has some SUMs in its main select statements which then use subSelects for a case in my SUM function
SELECT
SUM(CASE WHEN e.s = 'E'
AND EXISTS
( SELECT
1
FROM
system.E
JOIN system.EF
ON EF.EID = E.ID
WHERE
E.CID = C.ID
AND EF.T='smth')
AND A.AC= 'smthelse'
AND ET.EC not in( 'lol','lul','lel')
THEN
B.A
ELSE
0.0
END) AS smth
FROM ...
I expect it to SUM the b.A when the Select statement has at least 1 result
but instead I get this error message:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
I think it doesnt allow me to use a subSelect in the SUM-function, but im not sure how to fix it.
You can replace the subquery with a lateral JOIN by using the OUTER APPLY operator:
SELECT . . .
SUM(CASE WHEN e.s = 'E' AND
eef.ID IS NOT NULL AND
A.AC = 'smthelse' AND
ET.EC NOT IN ( 'lol', 'lul', 'lel')
THEN B.A ELSE 0.0
END) AS smth
FROM ... OUTER APPLY
(SELECT TOP (1) E.*
FROM system.E JOIN
system.EF
ON EF.EID = E.ID
WHERE E.CID = C.ID AND EF.T = 'smth'
) EEF

SQL Filling In Values From A Second Table

I came up with this query to fill in a missing field from a second table using a subquery.
I can not modify the original table
SELECT
CASE WHEN original.target_field IS NULL THEN
(SELECT fill_in.target_field FROM second.table fill_in
WHERE original.id = fill_in.id)
ELSE
original.target_field END AS myField
FROM
primary.table original
I was wondering if I was missing something and if there was a more performant way to do this?
You could use LEFT JOIN and COALESCE instead of correlated subquery:
SELECT COALESCE(original.target_field,fill_in.target_field) AS myField
FROM primary.table original
LEFT JOIN second.table fill_in
ON original.id = fill_in.id
It is always worth testing different methods. But your query should be fine with an appropriate index.
I would write it as:
SELECT (CASE WHEN o.target_field IS NULL
THEN (SELECT f.target_field
FROM second.table f
WHERE o.id = f.id
)
ELSE o.target_field
END) AS myField
FROM primary.table o;
You want an index on second.table(id, target_field). You would want the same index for the LEFT JOIN version.

Using NVL with JOIN not returning the default value if null

I have this query, just changed tables and columns name.
SELECT COM.NAME,
COM.SURNAME,
NVL(COM.ACCOUNT, -1) AS ACCOUNT,
(SELECT NVL(BA.ACCOUNT_ID, '-1')
FROM TABLE2 BA
WHERE BA.ACCOUNT_ID = COM.ACCOUNT_ID) AS ACCOUNT_ID
FROM TABLE1 COM
WHERE COM.UNIQUE_DNI = 123123;
Search show the registry as:
NAME ACCOUNT ACCOUNT_ID
qwe -1
Im trying to make the -1 appear when BA.ACCOUNT_ID is null but with no success.
I would just use an explicit left join:
SELECT COM.NAME, COM.SURNAME, COALESCE(COM.ACCOUNT, -1) AS ACCOUNT,
COALESCE(BA.CCOUNT_DI, '-1') AS ACCOUNT_ID
FROM TABLE1 COM LEFT JOIN
TABLE2 BA
ON BA.ACCOUNT_ID = COM.ACCOUNT_ID
WHERE COM.UNIQUE_DNI = 123123;
Your problem though is that the NVL() is inside the subquery. It needs to be applied to the results of the subquery. I prefer COALESCE() over NVL() simply because COALESCE() is ANSI/ISO standard.

SQL Subquery Having COUNT(var) turns 0 to NULLs

I have written a SQL query with a subquery to include counts. When the count is 0, and I try to filter out the 0, it turns the 0's to NULLs and keeps the rows, and vice versa. The result is that I can't filter out the 0's, which was the purpose of including the counts.
SELECT distinct
a
,b
,
(SELECT
count(id)
FROM seq_stud
WHERE scs.SequenceID = seq_stud.SequenceID
and seq_stud.EndDate is null
HAVING count(id) <> 0
) As t1
FROM sp
INNER JOIN p on sp.ProgramID = p.ProgramID
...etc.
Does anyone know why this is happening and how I can filter out the 0 counts?
You don't filter in the SELECT clause. If you don't want rows that have no match in seq_stud, then use WHERE:
WHERE EXISTS (SELECT 1
FROM seq_stud ss
WHERE scs.SequenceID = ss.SequenceID and ss.EndDate is null
)
I would remove the HAVING statement altogether. You need to put that in the WHERE clause. Otherwise, it will return null, as you found.
SELECT distinct a, b,
(SELECT count(id)
FROM seq_stud
WHERE scs.SequenceID = seq_stud.SequenceID
and seq_stud.EndDate is null
) As t1
FROM sp
INNER JOIN p on sp.ProgramID = p.ProgramID
WHERE t1 > 0
I just figured this out. The Select subquery should be included as a WHERE statement
Using having count() in exists clause

teradata SQL tuning pundits - SELECT Failed. 3771: Illegal expression in WHEN clause of CASE expression

I am using a statement as below and get this error:
SELECT Failed. 3771: Illegal expression in WHEN clause of CASE
expression.
I had better hopes from Teradata. SQL Server can do it but Teradata can't.
How can I work around this? Any solution?
sel ( CASE
WHEN EXISTS ( sel '1' from VolatileTable Dtb1 where Dtb1.c1=FACT_Table_5MillionRows.C1)
THEN "FACTTablew5MillionRows"."CustomColumName"
ELSE 'ALL OTHER'
END ) (NAMED "CustomColumName" )
from
"Db"."FACTTablew5MillionRows"
Replace
WHEN EXISTS (...)
By
WHEN 1 = (SELECT 1 WHERE EXISTS (...))
Teradata doesn't like EXISTS in Correlated Scalar Subqueries within a CASE, but you can rewrite it like this:
select
( CASE
WHEN C1 = ( select MIN(C1) from VolatileTable Dtb1
where Dtb1.c1=ft.C1)
THEN ft."CustomColumName"
ELSE 'ALL OTHER'
END ) (NAMED "CustomColumName" )
from
"Db"."FACTTablew5MillionRows" as ft
If VolatileTable.C1 is unique you can remove the MIN.
But in 95% logic like this can be replaced by a LEFT JOIN:
select
ft.*,
CASE WHEN Dtb1.c1 IS NOT NULL
THEN ft."CustomColumName"
ELSE 'ALL OTHER'
end as "CustomColumName"
from "Db"."FACTTablew5MillionRows" as ft
left join VolatileTable Dtb1
on Dtb1.c1=ft.C1
This will return duplicated rows if VolatileTable.C1 is not unique, then you need to change it to:
from "Db"."FACTTablew5MillionRows" as ft
left join (select distinct C1 from VolatileTable) Dtb1
on Dtb1.c1=ft.C1
WHEN EXISTS (select '1' from VolatileTable Dtb1
where Dtb1.c1=FACT_Table_5MillionRows.C1)
THEN somevalue --or a statement that yields a scalar value
You were selecting a column in the then part, where you should be assigning a unique value.