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?
m.MESSAGEID
(will always be there) rather than mp.MESSAGEID
(will only be there when there's a match).
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;
Hope that helps.
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;
Success story sharing
sum(case....end)
can be replaced with simplecount(mp.messageid)
.Count(*)
counts, well, everything, including nulls andcount(col_name)
counts only non-null values.WHERE (mp.PARTNO = 1 OR mp.PARTNO IS NULL)
and is added before theGROUP BY