0

I'm having a weird issue where in returning the sum of rows where a condition is true seems to only return rows if that condition is true - or at least, it is excluding a large majority of the rows that would otherwise be returned.

Original query - which returns all rows:

SELECT r.type, r.user1, r.user2, r.roomkey, r.name, r.active, m.active
FROM members as m 
INNER JOIN rooms as r ON m.roomid=r.id 
WHERE m.userid = ? 
ORDER BY r.active DESC, m.active DESC

For each row that was returned, I also wanted to get the number of records in another table with a newer timestamp than one of the timestamps I've already queried.

Modified query:

SELECT r.type, r.user1, r.user2, r.roomkey, r.name, r.active, m.active,
  COUNT(e.datetime > m.active) AS s 
FROM members as m 
INNER JOIN rooms as r ON m.roomid = r.id 
LEFT JOIN messages as e ON r.id = e.roomid 
WHERE m.userid = ? 
ORDER BY r.active DESC, m.active DESC

Basically, the original query returns all of the chat rooms a user is in. I want to modify the query to also return the number of unread messages corresponding to that room.

I'm trying to do it in a single query, so I don't have to execute another query in the while loop. Is there a better approach than the one above? I'm mainly worried about performance here, which is why I wanted to cram it all into a single query, but it doesn't seem to be working out. I've already confirmed that HAVING would not help me here and I had a similar lack of luck with subqueries.

0

2 Answers 2

1

Your query is not valid (although MySQL might accept it), since you have an aggregation function in the SELECT clause but no GROUP BY clause. Also, the additional JOIN will evict rooms that have no messages, which, presumably, s not what you want.

You could use a LEFT JOIN and aggregate in the outer query, however I would recommend a correlated subquery instead:

SELECT 
    r.type, 
    r.user1, 
    r.user2, 
    r.roomkey, 
    r.name, 
    r.active,
    m.active,
    (
        SELECT COUNT(*) 
        FROM messages e 
        WHERE r.id = e.roomid AND e.datetime > m.active
    ) no_messages
FROM members as m 
INNER JOIN rooms as r ON m.roomid = r.id 
WHERE m.userid = ? 
ORDER BY r.active DESC, m.active DESC

For performance with this query, you want an index on messages(room_id, datetime).

2
  • Is there supposed to be an AS before no_messages? Commented May 9, 2020 at 22:21
  • @InterLinked: actually the AS keyword is optional, you are free to add it if you find it more readable.
    – GMB
    Commented May 9, 2020 at 22:22
1

Using an extra join won't do what you expect, but will perform multiple unnecessary row matches.

You can use a scalar subquery:

SELECT r.type, r.user1, r.user2, r.roomkey, r.name, r.active, m.active,
  (
    select SUM(e.datetime > m.active) FROM messages e WHERE r.id = e.roomid 
  ) AS s 
FROM members as m 
INNER JOIN rooms as r ON m.roomid = r.id 
WHERE m.userid = ? 
ORDER BY r.active DESC, m.active DESC
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.