SQL Subquery Having COUNT(var) turns 0 to NULLs - sql

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

Related

Avoid divide by zero with an IF 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.

Display Y/N column if record found in detail table

I'm trying to create a query so that I can have a column show Y/N if a particular item was ordered for a group of orders. The item I'm looking for would be OLI.id = '538'.
So my results would be:
Order#, Customer#, FreightPaid
12345, 00112233, Y
12346, 00112233, N
I cannot figure out if I need to use a subquery or the where exists function ?
Here's my current query:
SELECT distinct
OrderID,
Accountuid as Customerno
FROM [SMILEWEB_live].[dbo].[OrderLog] OL
inner join Orderlog_item OLI on OLI.orderlogkey = OL.[key]
inner join Account A on A.uid = OL.Accountuid
where A.GroupId = 'X9955'
and OL.CreateDate >= GETDATE() - 60
I would suggest an exists clause instead of a join:
select ol.OrderID, ol.Accountuid as Customerno,
(case when exists (select 1
from Orderlog_item OLI join
Account A
on A.uid = OL.Accountuid
where OLI.orderlogkey = OL.[key] and A.GroupId = 'X9955'
)
then 1 else 0
end) as flag
from [SMILEWEB_live].[dbo].[OrderLog] OL
where OL.CreateDate >= GETDATE() - 60;
This prevents a couple of problems. First, duplicate rows which are caused when there are multiple matching rows (and select distinct add unnecessary overhead). Second, missing rows, which happen when you use inner join instead of an outer join.

Replace no result

I have a query like this:
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
order by V.IDTipoVersamento,month(DataAllibramento)
This query must always return something. If no result is produced a
0 0 0 0
row must be returned. How can I do this. Use a isnull for every selected field isn't usefull.
Use a derived table with one row and do a outer apply to your other table / query.
Here is a sample with a table variable #T in place of your real table.
declare #T table
(
ID int,
Grp int
)
select isnull(Q.MaxID, 0) as MaxID,
isnull(Q.C, 0) as C
from (select 1) as T(X)
outer apply (
-- Your query goes here
select max(ID) as MaxID,
count(*) as C
from #T
group by Grp
) as Q
order by Q.C -- order by goes to the outer query
That will make sure you have always at least one row in the output.
Something like this using your query.
select isnull(Q.TipoVers, '0') as TipoVers,
isnull(Q.ImpTot, 0) as ImpTot,
isnull(Q.N, 0) as N,
isnull(Q.Mese, 0) as Mese
from (select 1) as T(X)
outer apply (
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese,
V.IDTipoVersamento
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
) as Q
order by Q.IDTipoVersamento, Q.Mese
Use COALESCE. It returns the first non-null value. E.g.
SELECT COALESCE(TV.Desc, 0)...
Will return 0 if TV.DESC is NULL.
You can try:
with dat as (select TV.[Desc] as TipyDesc, sum(Import) as ToImp, count(*) as N, month(Date) as Mounth
from /*DATA SOURCE HERE*/ as TV
group by [Desc], month(Date))
select [TipyDesc], ToImp, N, Mounth from dat
union all
select '0', 0, 0, 0 where (select count (*) from dat)=0
That should do what you want...
If it's ok to include the "0 0 0 0" row in a result set that has data, you can use a union:
SELECT TV.Desc as TipyDesc,
sum(Import) as TotImp,
count(*) as N,
month(Date) as Mounth
...
UNION
SELECT
0,0,0,0
Depending on the database, you may need a FROM for the second SELECT. In Oracle, this would be "FROM DUAL". For MySQL, no FROM is necessary

SQL Having Clause

I'm trying to get a stored procedure to work using the following syntax:
select count(sl.Item_Number)
as NumOccurrences
from spv3SalesDocument as sd
left outer join spv3saleslineitem as sl on sd.Sales_Doc_Type = sl.Sales_Doc_Type and
sd.Sales_Doc_Num = sl.Sales_Doc_Num
where
sd.Sales_Doc_Type='ORDER' and
sd.Sales_Doc_Num='OREQP0000170' and
sl.Item_Number = 'MCN-USF'
group by
sl.Item_Number
having count (distinct sl.Item_Number) = 0
In this particular case when the criteria is not met the query returns no records and the 'count' is just blank. I need a 0 returned so that I can apply a condition instead of just nothing.
I'm guessing it is a fairly simple fix but beyond my simple brain capacity.
Any help is greatly appreciated.
Wally
First, having a specific where clause on sl defeats the purpose of the left outer join -- it bascially turns it into an inner join.
It sounds like you are trying to return 0 if there are no matches. I'm a T-SQL programmer, so I don't know if this will be meaningful in other flavors... and I don't know enough about the context for this query, but it sounds like you are trying to use this query for branching in an IF statement... perhaps this will help you on your way, even if it is not quite what you're looking for...
IF NOT EXISTS (SELECT 1 FROM spv3SalesDocument as sd
INNER JOINs pv3saleslineitem as sl on sd.Sales_Doc_Type = sl.Sales_Doc_Type
and sd.Sales_Doc_Num = sl.Sales_Doc_Num
WHERE sd.Sales_Doc_Type='ORDER'
and sd.Sales_Doc_Num='OREQP0000170'
and sl.Item_Number = 'MCN-USF')
BEGIN
-- Do something...
END
I didn't test these but off the top of my head give them a try:
select ISNULL(count(sl.Item_Number), 0) as NumOccurrences
If that one doesn't work, try this one:
select
CASE count(sl.Item_Number)
WHEN NULL THEN 0
WHEN '' THEN 0
ELSE count(sl.Item_Number)
END as NumOccurrences
This combination of group by and having looks pretty suspicious:
group by sl.Item_Number
having count (distinct sl.Item_Number) = 0
I'd expect this having condition to approve only groups were Item_Number is null.
To always return a row, use a union. For example:
select name, count(*) as CustomerCount
from customers
group by
name
having count(*) > 1
union all
select 'No one found!', 0
where not exists
(
select *
from customers
group by
name
having count(*) > 1
)

MySQL subquery returns more than one row

I am executing this query:
SELECT
voterfile_county.Name,
voterfile_precienct.PREC_ID,
voterfile_precienct.Name,
COUNT((SELECT voterfile_voter.ID
FROM voterfile_voter
JOIN voterfile_household
WHERE voterfile_voter.House_ID = voterfile_household.ID
AND voterfile_household.Precnum = voterfile_precienct.PREC_ID)) AS Voters
FROM voterfile_precienct JOIN voterfile_county
WHERE voterfile_precienct.County_ID = voterfile_County.ID;
I am trying to make it return something like this:
County_Name Prec_ID Prec_Name Voters(Count of # of voters in that precienct)
However, I am getting the error:
#1242 - Subquery returns more than 1 row.
I have tried placing the COUNT statement in the subquery but I get an invalid syntax error.
If you get error:error no 1242 Subquery returns more than one row, try to put ANY before your subquery. Eg:
This query return error:
SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);
This is good query:
SELECT * FROM t1 WHERE column1 = ANY (SELECT column1 FROM t2);
You can try it without the subquery, with a simple group by:
SELECT voterfile_county.Name,
voterfile_precienct.PREC_ID,
voterfile_precienct.Name,
count(voterfile_voter.ID)
FROM voterfile_county
JOIN voterfile_precienct
ON voterfile_precienct.County_ID = voterfile_County.ID
JOIN voterfile_household
ON voterfile_household.Precnum = voterfile_precienct.PREC_ID
JOIN voterfile_voter
ON voterfile_voter.House_ID = voterfile_household.ID
GROUP BY voterfile_county.Name,
voterfile_precienct.PREC_ID,
voterfile_precienct.Name
When you use GROUP BY, any column that you are not grouping on must have an aggregate clause (f.e. SUM or COUNT.) So in this case you have to group on county name, precienct.id and precient.name.
Try this
SELECT
voterfile_county.Name, voterfile_precienct.PREC_ID,
voterfile_precienct.Name,
(SELECT COUNT(voterfile_voter.ID)
FROM voterfile_voter JOIN voterfile_household
WHERE voterfile_voter.House_ID = voterfile_household.ID
AND voterfile_household.Precnum = voterfile_precienct.PREC_ID) as Voters
FROM voterfile_precienct JOIN voterfile_county
ON voterfile_precienct.County_ID = voterfile_County.ID
See the below example and modify your query accordingly.
select COUNT(ResultTPLAlias.id) from
(select id from Table_name where .... ) ResultTPLAlias;