Count Returning blank instead of 0 - sql

Good day everyone. Here is my code:
SELECT
'Expired Item -'+ DateName(mm,DATEADD(MM,4,AE.fld_LOAN)) as [Month]
,COUNT(PIT.fld_ID)'COUNT'
,SUM (PIT.fld_GRAM)'GRAMS'
,SUM (PH.fld_AMNT)'PRINCIPAL'
FROM #AllExpired AE
INNER JOIN Transactions.tbl_ITEM PIT
ON AE.fld_MAINID=PIT.fld_MAINID
INNER JOIN Transactions.tbl_HISTO PH
ON AE.fld_MAINID =PH.fld_MAINID
GROUP BY DATENAME(MM,(DATEADD(MM,4,AE.fld_LOAN)))
The problem I'm facing is that my Count function does not return 0 if it has no values, Sum function does not return NULL if there are no resulting values retrieved. Instead, it just outputs blank. Why is that so and how can I fix it?
Here is a screenshot of sample output.
Of course this is not what I want. I want it to output zero and null. Please help me, I do not know what's wrong. Thank you.

You cannot expect any records to be outputted when using a GROUP BY clause, when no records exist in your source.
If you want an output of 0 from the SUM and COUNT functions, then you should not use GROUP BY.
The reason is that when you have no records, the GROUP BY clause has nothing to group by, and then is not able to give you any output.
For example:
SELECT COUNT(*) FROM (SELECT 'Dummy' AS [Dummy] WHERE 1 = 0) DummyTable
will return one record with the value '0', where as:
SELECT COUNT(*) FROM (SELECT 'Dummy' AS [Dummy] WHERE 1 = 0) DummyTable
GROUP BY [Dummy]
will return no records.

I would imagine you need to change your joins from INNER to OUTER to ensure rows are returned even when there is no corresponding record in tbl_PawnItem -
SELECT
'Expired Item -'+ DateName(mm,DATEADD(MM,4,AE.fld_LoanDate)) as [Month]
,COUNT(PIT.fld_PawnItemID)'COUNT'
,SUM (PIT.fld_KaratGram)'GRAMS'
,SUM (PH.fld_PrincipalAmt)'PRINCIPAL'
FROM #AllExpired AE
LEFT JOIN Transactions.tbl_PawnItem PIT
ON AE.fld_PawnMainID=PIT.fld_PawnMainID
LEFT JOIN Transactions.tbl_PawnHisto PH
ON AE.fld_PawnMainID=PH.fld_PawnMainID
GROUP BY DATENAME(MM,(DATEADD(MM,4,AE.fld_LoanDate)))

Perhaps #AllExpired is empty, or one of the joins returns no results?
Remember inner joins need results on both sides in order to return, so because #AllExpired is empty the join returns nothing.
Change it to an OUTER join.

Related

Aggregate query with subquery (SUM)

I have the following query:
SELECT UserId, (
0.099 *
(
CASE WHEN
(SELECT AcceleratedProfitPercentage FROM CustomGroups cg
INNER JOIN UserCustomGroups ucg ON ucg.CustomGroupId = cg.Id
WHERE Packs.UserCustomGroupId = ucg.Id)
IS NOT NULL THEN
((SELECT AcceleratedProfitPercentage FROM CustomGroups cg
INNER JOIN UserCustomGroups ucg ON ucg.CustomGroupId = cg.Id
WHERE Packs.UserCustomGroupId = ucg.Id)*1.0) / (100*1.0)
ELSE 1
END
)
)
As amount
FROM Packs WHERE Id IN (
SELECT ap.Id FROM Packs ap JOIN Users u ON ap.UserId = u.UserId
WHERE ap.MoneyToReturn > ap.MoneyReturned AND
u.Mass LIKE '1%');
which is producing correct output. However I have no idea how to aggregate it properly. I tried to use standard GROUP BY but I get the error (Column 'Packs.UserCustomGroupId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY claus). Any ideas? Here is the output I currently get:
I want to aggregate it by UserId. Thanks in advance.
The option that involves the least query-rewriting is to drop your existing query into a CTE or temp table, like so:
; with CTE as (MyQueryHere)
Select UserID, sum(amount)
from CTE
Group by UserID
Wow that is one crazy query you've got going on there.
Try this:
SELECT UserId,
0.099 * SUM(t.Amount) AS [Amount SUM]
FROM Packs P
JOIN Users U
ON P.UserID = U.UserID
LEFT OUTER JOIN UserCustomGroups UCG
ON P.UserCustomGroupID = UCG.ID
LEFT OUTER JOIN CustomGroups CG
ON UCG.CustomGroupID = CG.ID
CROSS APPLY
(
SELECT CASE WHEN CG.ID IS NULL
THEN 1
ELSE CG.AcceleratedProfitPercentage / 100
END AS [Amount]
) t
WHERE P.MoneyToReturn > P.MoneyReturned
AND U.Mass LIKE '1%'
GROUP BY UserID
First, multiplying any number by 1 is pretty pointless, yet I see it twice in your original post. I'm not sure what led to that, but it's unnecessary.
Also, using CROSS APPLY will eliminate the need for you to repeat your subquery. Granted, it's slower (since it'll run on every row returned), but I think it makes sense in this case...Using left outer joins instead of CASE - SELECT - IS NULL makes your query much more efficient and much more readable.
Next, it appears that you are attempting to SUM percentages. Not sure what kind of data you're looking to return, but perhaps AVG would be more appropriate? I can't think of any practical reason why you would be looking to do that.
Lastly, APH's answer will most certainly work (assuming your original query works), but given the obfuscation and inefficiency of your query, I would definitely rewrite it.
Please let me know if you have any questions.

Sql Join and Sum

I'm trying to sum an amount from two different tables using a left join. I need all rows returned regardless of whether or not there is a match on the second table.
SELECT l.tender,
l.starting+SUM(t.amount) AS 'amount'
FROM label l
LEFT JOIN transfers t on l.tender=t.name
ORDER BY l.tender
This should work:
SELECT l.tender,
l.starting + ISNULL(t.amount,0) AS amount
FROM label l
LEFT JOIN ( SELECT name, SUM(amount) amount
FROM transfers
GROUP BY name) t
ON l.tender = t.name
ORDER BY l.tender
Wrap the SUM(amount) in a COALESCE function so that if there is no match and it is NULL, it will add 0 and won't mark the whole row as NULL.
Change line 2 of your query to look like this:
l.starting + COALESCE(SUM(t.amount), 0) AS amount
Edit:
As #Lamak noted, you also need to GROUP BY your results so that you can SUM it properly. The whole query can look like this (alternative to the other answer):
SELECT l.tender,
l.starting + COALESCE(SUM(t.amount), 0) AS amount
FROM label l
LEFT JOIN transfers t on l.tender=t.name
GROUP BY l.tender, l.starting
ORDER BY l.tender

Missing rows when selecting from 2 tables

Hi and sorry if that's a stupud question but I am trying to get results from two different tables and since one of them might have zero records I am not sure how to proceed. Here is the query:
SELECT aid.*, T.ItemId, T.Total, T.Stack
FROM (SELECT ItemId, Stack, count(ItemId) as Total FROM auction_house WHERE Sale = 0 GROUP BY ItemId, Stack) as T, ahbot_item_data aid
WHERE T.ItemId = aid.ItemId
AND T.Stack = aid.Stack
Basically I want to get a list of items from the aid table, and the current count of those items in the ah table. But since the count might just be zero, it might not return that row. I want the row and the Total to be 0.
Thank you in advance ^^;;
Similar to what #Twelfth was saying, you want to have a left outer join, which always outputs data from the left table even if there is no matching data in the right table.
SELECT
aid.ItemId,
count(ah.ItemId) AS "Total",
aid.Stack
FROM
ahbot_item_data aid
LEFT OUTER JOIN auction_house ah
ON (aid.ItemID = ah.ItemID AND aid.Stack = ah.Stack)
GROUP BY
aid.ItemID, aid.Stack
You are using old syntax...I'm going to switch to join syntax (newer and more accepted...same effect, easier to read). What you want is an 'outer join', which will produce nulls where no record is found. I'll arrange the tables and we'll use a left outer join
SELECT aid.*, T.ItemId, T.Total, T.Stack
FROM ahbot_item_data aid left join (SELECT ItemId, Stack, count(ItemId) as Total FROM auction_house WHERE Sale = 0 GROUP BY ItemId, Stack) as T,
on T.ItemId = aid.ItemId
AND T.Stack = aid.Stack
I'm not entirely sure on the syntax anymore...but it's also possible to use != to do this outer join in the syntax you've used (where aid.itemID != t.itemID I think?)

Getting all records including non matching records

I have the following tables
Payment
PayTypeId, Description
0 , Credit
1, Debit
2,Master
ActualPayment
Id,PayTypeId,Amount
1,1,10
Here is the output i am looking at
Id,PayTypeId,Amount
1,0,NULL
1,1,10
1,2,NULL
Basically I want all the records of ActualPayment including all payment types.
Here is the query i have used but am not getting any records
select
*
from #ActualPayments ap
left join #Payment p on ap.paytypeid = p.paytypeid
where p.paytypeid is null
If you want one record for each of the three PayTypeID values, then you need those three records on the left-hand side of the LEFT JOIN.
Equally, if you want the ActuallPaymentID on each output line, that value needs to be on the left hand side.
It's all leading you down the wrong avenue with the data that you have, and the tables that you have described.
With just those two tables in your question, I would use this layout instead...
SELECT
ap.ActualPaymentID,
p.PayTypeID,
SUM(CASE WHEN ap.PayTypeID = p.PayTypeID THEN ap.Amount END) AS Amount
FROM
ActualPayments AS ap
CROSS JOIN
Payment AS p
GROUP BY
ap.ActualPaymentID,
p.PayTypeID
You aren't receiving any records because you are filtering everything out with the WHERE clause p.paytypeid is null
Try running it without the WHERE clause.
Edit: The below SQL should return the correct information. I've used a CROSS JOIN to create an in-line view. This should remove the unwanted NULLs.
SELECT t1.id, t1.paytypeid, t2.amount
FROM (
SELECT id, payment.paytypeid
FROM #ActualPayments
CROSS JOIN #Payment
) t1
LEFT OUTER JOIN #ActualPayments t2
ON t1.paytypeid = t2.paytypeid
;
I think you want a FULL OUTER JOIN:
select
*
from #ActualPayments ap
full outer join #Payment p
on ap.paytypeid = p.paytypeid
;
This will return all rows from the ActualPayments table along with their corresponding values from Payment - if there is any. Additional it will return all rows from Payment for which no ActualPayments exist.
Please note: The where clause of your sample query must not be used!
I'm confused why you would want to do this, but here's one way
select ap.Id, pt.PayTypeId, ap2.Amount
from #ActualPayments ap
cross join #PaymentTypes pt
left join #ActualPayments ap2
on pt.PayTypeId = ap2.PayTypeId
and ap.Id = ap2.id

get COUNT to return 0 when EXISTS() subquery is false

Given the following query:
SELECT M.year, COUNT(C.eid)
FROM Card AS C, Month AS M
WHERE EXISTS(SELECT 1 FROM Charge AS CH
WHERE CH.usingcard=C.eid AND CH.year=M.year)
GROUP BY M.year
where EXISTS is used to avoid the number of 'Charge' matching the given year/card changing the count. The pb is that I would like a resulting row for each possible 'Month' row, eg when there are no matching 'Charge', I would like to get 0 as count, while I currently get no row at all.
Without EXISTS, I could use an outer join. I could also probably use an UNION with a query returning 0 for case where NOT EXISTS().
Anyone has a smarter idea ?
This avoids the the problem of multiple Charge records:
SELECT M.year, COUNT(distinct CH.usingcard)
FROM Card AS C
CROSS JOIN Month AS M
LEFT JOIN Charge CH on CH.usingcard=C.eid AND CH.year=M.year
GROUP BY M.year
This counts how may different cards were charged in the year. Non-joining rows will have CH.usingcard of null, which won't be counted.
The NOT EXISTS is excluding zero counts: as expected
If you want zero counts, then you need the OUTER JOIN. However, your FROM clause is a CROSS JOIN so you'd get wrong counts. And you can ignore the Card table because usingcard has the same data (otherwise you wouldn't use it in your EXISTS)
It is this simple...
SELECT
M.year, COUNT(CH.usingcard)
FROM
Month AS M
LEFT JOIN
Charge AS CH On CH.year = M.year
GROUP BY
M.year
If all arguments are NULL, COALESCE returns NULL.
COALESCE(expression1,...n) is equivalent to the following CASE expression:
CASE
WHEN (expression1 IS NOT NULL) THEN expression1
WHEN (expression2 IS NOT NULL) THEN expression2
...
ELSE expressionN
END
The following example shows how COALESCE selects the data from the first column that has a nonnull value.
SELECT Name, Class, Color, ProductNumber,
COALESCE(Class, Color, ProductNumber) AS FirstNotNull
FROM Production.Product ;
You could the NULLIF function which can be used inside the count function. This function takes two parameters: if the parameters are equal, then it returns null, else it returns the value of the first parameter.
SELECT M.year, COUNT (NULLIF (CH.usingcard, NULL))
FROM Month as M left join Charge as CH
ON CH.Year = M.Year
GROUP BY M.year