Counting number of joined rows in left join - sql

I'm trying to write an aggregate query in SQL which returns the count of all records joined to a given record in a table; If no records were joined to the given record, then the result for that record should be 0:
Data
My database looks like this (I'm not able to change the structure, unfortunately):
MESSAGE
----------------------------------------------
MESSAGEID SENDER SUBJECT
----------------------------------------------
1 Tim Rabbit of Caerbannog
2 Bridgekeeper Bridge of Death
MESSAGEPART
----------------------------------------------
MESSAGEID PARTNO CONTENT
----------------------------------------------
1 0 (BLOB)
1 1 (BLOB)
3 0 (BLOB)
(MESSAGEPART has a composite PRIMARY KEY("MESSAGEID", "PARTNO"))
Desired output
Given the data above I should get something like this:
MESSAGEID COUNT(*)
-----------------------------------------------
1 2
2 0
It seems obvious that I need to do a left join on the MESSAGE table, but how do I return a count of 0 for rows where the joined columns from MESSAGEPART are NULL? I've tried the following:
Logic
I've tried
SELECT m.MESSAGEID, COUNT(*) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;
However, this returns
MESSAGEID COUNT(*)
-----------------------------------------------
1 2
2 1
I've also tried
SELECT mp.MESSAGEID, COUNT(*) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY mp.MESSAGEID;
but this returns
MESSAGEID COUNT(*)
-----------------------------------------------
1 2
1
What am I doing wrong here?

How about something like this:
SELECT m.MESSAGEID, sum((case when mp.messageid is not null then 1 else 0 end)) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;
The COUNT() function will count every row, even if it has null. Using SUM() and CASE, you can count only non-null values.
EDIT: A simpler version taken from the top comment:
SELECT m.MESSAGEID, COUNT(mp.MESSAGEID) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;

You first want to count in your messaepart table before joining, i think. Try this:
SELECT m.MessageId
, COALESCE(c, 0) as myCount
FROM MESSAGE m
LEFT JOIN (SELECT MESSAGEID
, count(*) c
FROM MESSAGEPART
GROUP BY MESSAGEID) mp
ON mp.MESSAGEID = m.MESSAGEID

Don't forget to use DISTINCT in case you will LEFT JOIN more than one table:
SELECT m.MESSAGEID, COUNT(DISTINCT mp.MESSAGEID) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;

Return one number as a total of the matching elements between two tables, based on matching columns
In my case, I needed one total number returned for the number/count of matching items from a particular column and from two different tables.
For example, I have two separate tables that each have a PhoneNumber column. Between those two tables, I want to know how many from that column match.
Reference: https://www.guru99.com/joins.html
Using the same tables name above, it would look like this:
SELECT COUNT(DISTINCT m.MESSAGEID) AS COUNT FROM MESSAGE m, MESSAGEPART mp
where mp.MESSAGEID = m.MESSAGEID;

Related

SQL statement with union join or left join

database table
I have this SQL problem to find the total count of the disable people between 2 table
The condition is I only want to get the ID with appearing in the recipient table
left join graph
I only want to get the total count of the left side data which link together with the a_children
http://sqlfiddle.com/#!9/2ca178/1
SELECT COUNT(*)
FROM recipient r
LEFT JOIN a_children c
ON r.hp_id = c.hp_id
AND c.health='OKU'
WHERE r.disability = 'YES'

Using group by with left join and IN yields empty result

I have two tables: invitees and invitee_information. invitees belong to events (third table event_id is a foreign key in invitees table).
I want to get the total count of invitees per event for which there is no information available yet (no record in the invitee_information table).
When I write the query using only one event_id in the where clause and anti-join pattern (IS NULL for invitee_id column to filter for rows having no information saved) the count is returned as expected for that given event_id:
SELECT COUNT(i.id)
FROM invitees i
LEFT JOIN invitee_information ii
ON ii.invitee_id = i.id
WHERE ii.invitee_id IS NULL AND i.`event_id` = 18571 AND i.`invitation_sent` = 1;
Now in order to optimize the query to get this count data at DB level for multiple event_ids instead of getting this in a loop for each event_id, I used IN and passed multiple event_ids and used GROUP_BY event_id.
Expected result should be (if there is no data saved in invitee_information table for event_id 18569 :
event_id | count(i.id)
18569 | 0
But query result is always empty. My updated query is:
SELECT i.`event_id`, COUNT(i.id)
FROM invitees i
LEFT JOIN invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND i.`event_id` IN(18569,18571) AND i.`invitation_sent` = 1
GROUP BY i.`event_id`;
The only way I could imagine this happening is if you have internationalization settings such that , means a decimal point. You can easily test this using:
SELECT i.`event_id`, COUNT(i.id)
FROM invitees i LEFT JOIN
invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND
(i.`event_id` = 18569 OR i.`event_id` = 18571) AND
i.`invitation_sent` = 1
GROUP BY i.`event_id`;
If this works, then at least we have identified the problem. I think a space then works:
SELECT i.`event_id`, COUNT(i.id)
FROM invitees i LEFT JOIN
invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND
i.`event_id` IN (18569, 18571) AND
i.`invitation_sent` = 1
GROUP BY i.`event_id`;
I should note that you probably want COUNT(ii.invitee_id) in the SELECT rather than COUNT(i.id). The latter will never return 0.
EDIT:
Perhaps the two events are not in the invitees table. Assuming you have an events table, then something like this:
SELECT e.event_id, COUNT(i.id)
FROM events e LEFT JOIN
invitees i
ON i.event_id = e.event_id AND
i.invitation_sent = 1 LEFT JOIN
invitee_information ii
ON i.id = ii.invitee_id
WHERE ii.invitee_id IS NULL AND
e.event_id IN (18569, 18571) AND
GROUP BY e.event_id;

SQL select with join are returning double results

I am trying to select some data from different tables using join.
First, here is my SQL (MS) query:
SELECT Polls.pollID,
Members.membername,
Polls.polltitle, (SELECT COUNT(*) FROM PollChoices WHERE pollID=Polls.pollID) AS 'choices',
(SELECT COUNT(*) FROM PollVotes WHERE PollVotes.pollChoiceID = PollChoices.pollChoicesID) AS 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID;
And the tables involved in this query is here:
The query returns this result:
pollID | membername | polltitle | choices | votes
---------+------------+-----------+---------+-------
10000036 | TestName | Test Title| 2 | 0
10000036 | TestName | Test Title| 2 | 1
Any help will be greatly appreciated.
Your INNER JOIN with PollChoices is bringing in more than 1 row for a given poll as there are 2 choices for the poll 10000036 as indicated by choices column.
You can change the query to use GROUP BY and get the counts.
In case you don't have entries for each member in the PollVotes or Polls table, you need to use LEFT JOIN
SELECT Polls.pollID,
Members.membername,
Polls.polltitle,
COUNT(PollChoices.pollID) as 'choices',
COUNT(PollVotes.pollvoteId) as 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID
INNER JOIN PollVotes
ON PollVotes.pollChoiceID = PollChoices.pollChoicesID
AND PollVotes.memberID = Members.memberID
GROUP BY Polls.pollID,
Members.membername,
Polls.polltitle
You are getting 1 row for each PollChoices record since there are multiple choices per Polls INNER JOIN Members. You may be expecting the SELECT COUNT(*) sub-queries to act as a GROUP BY clause, but they don't.
If that doesn't make sense, add a bare minimum of sample data and the expected result and we can help more.
This query result is telling you the number of votes per choice in each poll.
In your example, this voter named TestName answered the poll (with ID 10000036) and gave one choice 1 vote, and the second choice 0 votes. This is why you are getting two rows in your result.
I'm not sure if you are expecting just one row because you didn't specify what data, exactly, you are trying to select. However if you are trying to see the number of votes that TestName has submitted, for each choice where the vote was greater than 1, then you will have to modify your query like this:
select * from
(SELECT Polls.pollID,
Members.membername,
Polls.polltitle, (SELECT COUNT(*) FROM PollChoices WHERE pollID=Polls.pollID) AS 'choices',
(SELECT COUNT(*) FROM PollVotes WHERE PollVotes.pollChoiceID = PollChoices.pollChoicesID) AS 'votes'
FROM Polls
INNER JOIN Members
ON Polls.memberID = Members.memberID
INNER JOIN PollChoices
ON PollChoices.pollID = Polls.pollID) as mysubquery where votes <> 0;

Combine 2 SQL SELECT statements

** Evidently some people think my question is not worthy of their time. I whole heartedly appologise for this. However, rather than down voting why not use that time to do something positive and at least tell me what info you would require to make this not be a cr#p question in your eyes. **
I have a list of staff in table tblMembers and a list of clients in table tblClients.
One person may have several client.
The staff member associated with a client is identified by staffId against the client record.
Each staff member has a category Id for the type of clients they have catId.
I need to find all of the staff for a given client type and then sort them by the number of clients they have. Staff members without any clients should show a result of 0 rather than not showing.
A simplified table structure would be:
tblMembers:
Id | catId
tblClients:
Id | staffId
Any help would be greatly appreciated.
Thanks!
Try this:
SELECT m.Id 'Member Id', ISNULL(c.StaffCount, 0) 'StuffCount'
FROM tblMembers m
LEFT JOIN
(
SELECT staffId, COUNT(staffId) 'StaffCount'
FROM tblClients
GROUP BY staffId
) c ON m.Id = c.staffId
WHERE m.Cat = 'Some Id'
ORDER BY StuffCount
Its fairly simple to do a join/group and count
SELECT
s.id,
s.catid,
COUNT(c.id)
FROM
tblMembers s
LEFT JOIN tblClients c
ON s.id = c.staffid
WHERE
s.catid = #catID
GROUP BY
s.id,
s.catid
ORDER BY
COUNT(c.id) desc
However the one tricky bit is
show a result of 0 rather than not showing.
To do this you need to do a left join to make sure they show even if there are no matching records and you need to make sure to count a field on the table on the Right side of the join. Otherwise you'd get a count of 1
DEMO
Hope I correctly understood your case.
Try something like this:
SELECT T1.ID,
Count(*)
FROM MEMBERS T1
INNER JOIN CLIENTS T2
ON T1.ID = T2.STAFFID
WHERE T1.CATID = 2
GROUP BY T1.ID
UNION
SELECT DISTINCT ID,
0
FROM MEMBERS
WHERE CATID != 2
A working sample is available here.
try:
select tblMembers.id, count(tblClient.id)
from tblMembers left join tblCLient on staffId = tblMembers.id
where tblMembers.catId = ??
group by tblMembers.id
order by 2 desc

Sub-Query Problem

I have two tables like this
Member and their Purchases
I need the output like this
Member_ID | CountofProducts
(and the Product Value not should be 0 and Purchase Status = 1)
SELECT
MemberName,
(SELECT COUNT(*) AS Count
FROM dbo.Purchases
WHERE MemberName = dbo.Members.MemberName
AND Res_Status = 1) AS Count
FROM
dbo.Members
I can get their total CountofPurchased Products from the above query but I need to avoid their count=0 how to do it ?
You can try something like
SELECT m.MemberName,
COUNT(p.*) Cnt
FROM Members m INNER JOIN
Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
There is no need for the HAVING clause, as the INNER JOIN will exclude all entries in Members that do not have Purchases.
SELECT m.MemberName, COUNT(p.*) AS CountOfProducts
FROM Members m
INNER JOIN Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
HAVING COUNT(p.*) > 0
I think the above will somewhat do what you want. The key is that you probably do not even need your subquery, but rather you can handle the query (possibly with greater efficiency) just using a join.