Multiple COUNT() in JOIN - mysql

I'm trying to get the number of rows of two different tables with two LEFT JOIN in a MySQL query. It works well when I have a COUNT on one table like this :
SELECT a.title, a.image, COUNT(o.id) AS occasions
FROM activity a
LEFT JOIN occasion AS o ON a.id = o.activity_id
WHERE a.user_id = 1
GROUP BY a.id
ORDER BY a.created_at
DESC LIMIT 50
Here, everything works and I get the good number of "occasions".
But when I try to add an additional COUNT with an additional LEFT JOIN, the result of the second COUNT is wrong :
SELECT a.title, a.image, COUNT(o.id) AS occasions, COUNT(au.id) AS users
FROM activity a
LEFT JOIN occasion AS o ON a.id = o.activity_id
LEFT JOIN activity_user AS au ON a.id = au.activity_id
WHERE a.user_id = 4
GROUP BY a.id
ORDER BY a.created_at
DESC LIMIT 50
Here, I get the good number of "occasions" but "users" seems to be a copy of the "occasions" count, which is wrong.
So my question is, how to fix this query to have the two COUNT working together?

COUNT() counts non-NULL values. The simple way to fix your query is to use COUNT(DISTINCT):
SELECT a.title, a.image,
COUNT(DISTINCT o.id) AS occasions, COUNT(DISTINCT au.id) AS users
. . .
And this will probably work. However, it creates an intermediate table that is the Cartesian product of the two tables (for each title). That could grow very big. The more scalable solution is to use subqueries and aggregate before joining.

The used left join for activity user limits your result because the DB is not able to found related data. But when you use LEFT OUTER JOIN the it should return all expected rows and their count.

Related

Join MySQL Tables to Display Count

Firstly, I'm a beginner to MySQL and I'm still learning. I'm trying to join 2 tables to display a count. Primarily, I use 2 codes. One code to display names -
SELECT tag_logs.timestamp, People.Name FROM `tag_logs` INNER JOIN People WHERE tag_logs.tag_no = People.nametag
Another code to display count of names -
SELECT tag_logs.tag_no, COUNT(tag_logs.tag_no) FROM tag_logs GROUP BY tag_no HAVING COUNT(tag_no) >= 1
I want to display Name and a count number, instead of a tag number and count. I attempted to join both tables by using the following code, however, I've had little luck -
SELECT People.Name FROM `tag_logs` INNER JOIN People WHERE tag_logs.tag_no = People.nametag AND COUNT(tag_logs.tag_no) FROM tag_logs GROUP BY tag_no HAVING COUNT(tag_no) >= 1
I'm given an error when I try to call 'FROM tag_logs' a second time. Is there a way to work around this?
I aim to make this my final result, except I should be able to see names instead of numbers.
Two tables are joined using ON clause. You should learn joins.
SELECT People.Name ,COUNT(tag_logs.tag_no)
FROM `tag_logs`
INNER JOIN People ON tag_logs.tag_no = People.nametag
GROUP BY tag_logs.tag_no
HAVING COUNT(tag_no) >= 1
It should be
SELECT People.Name FROM `tag_logs`
INNER JOIN People on tag_logs.tag_no = People.nametag
GROUP BY tag_no HAVING COUNT(tag_no) >= 1
EDIT
SELECT People.Name, COUNT(tag_no) FROM `tag_logs`
INNER JOIN People on tag_logs.tag_no = People.nametag
GROUP BY tag_no HAVING COUNT(tag_no) >= 1
I believe the query that you want looks like this:
SELECT p.Name, COUNT(*)
FROM tag_logs tl INNER JOIN
People p
ON tl.tag_no = p.nametag
GROUP BY p.Name;
Notes:
COUNT(*) is shorter than COUNT(tl.tag_no) and they do the same thing.
GROUP BY clause now matches the SELECT. If you could have people with the same names, then add p.nametag to the GROUP BY. A version use only GROUP BY tl.tag_no is invalid SQL and should fail in most databases, because of the non-matching p.Name in the SELECT.
The HAVING clause (HAVING COUNT(tag_no) >= 1) is unnecessary, because the INNER JOIN requires at least one match and tag_no is never NULL (because it is used for the JOIN).
I introduced table aliases, so the query is easier to write and to read.

returning all rows in table 1, when join most recent record in table 2

I've the following code, which pulls out the most recent row from a table called wwlassessments.
It works, but what I'm trying to do is show all the rows matching the WHERE criteria in table, regardless of whether there's an entry in the wwlassessments table.
I've tried changing the 2nd JOIN to a LEFT JOIN, but this just provides thousands of inaccurate results.
I'm sure it's very simple, but I can't for the life of me work out what I need to change! Thanks in advance.
SELECT s.*,
a.*
FROM wwlstatements s
LEFT JOIN wwlassessments a ON a.id = s.id
JOIN (SELECT n.id,n.pupilID,
MAX(n.dateAchieved) AS max_achieved_date
FROM wwlassessments n
where n.pupilID='114631705547'
GROUP BY n.id) y ON y.id = a.id
AND y.max_achieved_date = a.dateAchieved
WHERE s.`category`='Reading'
ORDER BY s.`statementID` ASC

Multiple Grouping in mysql queries. Group_concat? Group_by? Inner joins? Where am I going wrong?

I'm finding trouble finding a similar example to what I'm trying to achieve. I have 3 tables. From one table I want to get the linking ID number. From another table I want to find the same ID's and add up another column of numbers in that table where the ID number from the 1st table matches. Then on the 3rd table, which is text, I want to group all the text together where the ID matches the main ID number... and return all this in 1 go. My diagram should show what I mean:
So have 2 queries that will on their own return part the results, but Im struggling to build it into 1 single query.
SELECT ticket_charges.ticket_id
, sum(ticket_charges.charge_time) AS Seconds
FROM
ticket_charges
LEFT OUTER JOIN tickets
ON ticket_charges.ticket_id = tickets.id
GROUP BY
ticket_charges.ticket_id
, tickets.id
The 77 and 937 for ticket ID 3 have been added up correctly!!
SELECT tickets.id AS `Ticket Number`
, left(tickets_messages.message, 500) AS `Ticket Message`
FROM
tickets
INNER JOIN tickets_messages
ON tickets.id = tickets_messages.id
GROUP BY
tickets_messages.ticket_id
, tickets.id
The messages are joined together correctly.
I've tried some concatenation on messages, selects within selects, different methods to group by, a couple of sums etc.. but just can't seem to get a result where by the I'm getting the results back correctly with both queries as 1 single query. Either the joined numbers from "charge_time" are very wrong and don't match any resemblance to anything or I end up with hundreds of "message" and strange numbers on the "charge_time"
FYI.. If I try this, I get "Sub query returned more than 1 row" but it's what I thought I should be doing.
SELECT ticket_charges.ticket_id
, sum(ticket_charges.charge_time) AS Seconds
FROM
ticket_charges
LEFT OUTER JOIN tickets
ON ticket_charges.ticket_id = tickets.id
Where (SELECT left(tickets_messages.message, 500)
FROM
tickets
INNER JOIN tickets_messages
ON tickets.id = tickets_messages.id
GROUP BY
tickets.id)
GROUP BY
ticket_charges.ticket_id
, tickets.id
If you really need to do that with a single query, the solution is to do a subquery in one of the jointures.
SELECT t.id, t.person_id, SUM(tc.charge_time), mc.concat
FROM tickets t
INNER JOIN tickets_charges tc ON tc.ticket_id = t.id
INNER JOIN (
SELECT ticket_id, GROUP_CONCAT(message SEPARATOR ' ') as concat
FROM tickets_messages
GROUP BY ticket_id) AS mc
ON mc.ticket_id = t.id
GROUP BY t.id
Try this query -
SELECT
t.id,
t.person_id,
SUM(tc.charge_time) Seconds,
GROUP_CONCAT(LEFT(tm.message, 20)) Message
FROM
tickets t
LEFT JOIN ticket_charges ts
ON ts.ticket_id = t.id
LEFT JOIN tickets_messages tm
ON tm.ticket_id = t.id
GROUP BY
t.id;
Note, that I used 'LEFT(tm.message, 20)', because GROUP_CONCAT function has length limitation - group_concat_max_len.

Left outer join and sum issue

I need a query. I'm trying to sum of one field with joined tables. Some records not in second table. So this records sum should be zero. But the query only sum the records which are in the second table.
select s.*,sum(sd.fiyat) as konak from fuar_sozlesme1 s
left outer join fuar_sozlesme1_detay sd on (sd.sozlesme_id = s.id)
------EDIT-------
I added group by into the query and solved my problem. Here is the new ;
select s.*,sum(sd.fiyat) as konak from fuar_sozlesme1 s
left outer join fuar_sozlesme1_detay sd on (sd.sozlesme_id = s.id)
group by sd.sozlesme_id
I thinik you need to use IFNULL(sd.fiyat,0) instead of sd.fiyat to get zeros for the NULL values coming from the second table because of the LEFT JOIN like so:
SELECT s.*, SUM(IFNULL(sd.fiyat, 0)) as konak
FROM fuar_sozlesme1 s
LEFT OUTER JOIN fuar_sozlesme1_detay sd ON sd.sozlesme_id = s.id
GROUP BY s.someFields
Here is a simple example, you may help: http://sqlfiddle.com/#!2/41481/1
This is an old thread, but I spent a couple of hours trying to solve the same issue.
My query has two joins, a filter and a SUM function. I'm no SQL expert, but this helped me achieve the desired result of still showing a result even if the joined table had no rows to sum.
The key for me in order to show results even if the sum was totaling nothing, was the GROUP BY. I'm still not 100% sure why.
The two types of joins were chosen based on this article - MySQL Multiple Joins in one query?
SELECT registrations.reg_active, registrations.team_id, registrations.area_id, registrations.option_id, registrations.reg_fund_goal, registrations.reg_type, registrations.reg_fee_paid, registrations.reg_has_avatar, users.user_name, users.user_email, users.user_phone, users.user_zip, users.user_age, users.user_gender, users.user_active, SUM(IFNULL(donations.donation_amount,0)) as amt from registrations
INNER JOIN `users`
ON registrations.user_id = users.user_id
AND registrations.event_id = :event_id
LEFT OUTER JOIN `donations`
ON registrations.reg_id = donations.reg_id
GROUP BY donations.reg_id
ORDER BY users.user_name ASC

MySQL nested SELECT too slow

I have a script of the following structure:
SELECT SUM(CASE WHEN pf.info IS NOT NULL THEN 1 ELSE 0 END)
FROM summary s
LEFT JOIN (SELECT id, info FROM items GROUP BY id) pf ON s.id=pf.id
GROUP BY s.date
What I want is to count those id's which are in 'summary' and present in 'items'. 'items' have same id's repeated several times, that's why I do GROUP BY.
This script works as I want, but it is extremely slow, much slower than just doing straightforward LEFT JOIN (and counting each id several times). This doesn't seem to make sense since I need a smaller subspace of that and it should be easier.
So the question is: how to restructure the query to make it quicker?
Use count(distinct ...):
SELECT count(distinct s.id)
FROM summary s
JOIN items i ON s.id = i.id
I don't understand why you are grouping by s.date - there's no clue in your question as to why, so if it's not a mistake and you need to group by date, use this:
SELECT s.date, count(distinct s.id)
FROM summary s
JOIN items i ON s.id = i.id
GROUP BY s.date