Reputation: 9007
I'm building a small messaging system for my app, the primary idea is to have 2 tables. Table1 messages
Id,sender_id,title,body,file,parent_id
Here is where messages are stored, decoupled from whom will receive it to allow for multiple recipients.
Parent I'd link to parent message if its a reply, and file is a blob to store single file attached to message
Table 2 message_users
Id,thread_id,user_id,is_read,stared,deleted
Link parent thread to target users,
Now for a single user to get count of unread messages I can do
Select count(*) from message_users where user_id = 1 and is_read is null
To get count of all messages in his inbox I can do
Select count(*) from message_users where user_id = 1;
Question is how to combine both in single optimized query ?
Upvotes: 0
Views: 972
Reputation: 656
So you're trying to achieve something that will total rows that meet one condition and the total rows that meet an extra condition:
|---------|---------|
| total | unread |
|---------|---------|
| 20 | 12 |
|---------|---------|
As such will need something with a form along the lines of:
SELECT A total, B unread FROM message_users WHERE user_id=1
A
is fairly straightforward, you already more-or-less have it: COUNT(Id)
.
B
is marginally more complicated and might take the form SUM( IF(is_read IS NULL,1,0) )
-- add 1 each time is_read is not null; the condition will depend on your database specifics.
Or B
might look like: COUNT(CASE WHEN is_read IS NULL THEN 1 END) unread
-- this is saying 'when is_read is null, count another 1'; the condition will depend on your database specifics.
In total:
SELECT COUNT(Id) total, COUNT(CASE WHEN is_read IS NULL THEN 1 END) unread FROM message_users WHERE user_id=1
Or:
SELECT COUNT(Id) total, SUM( IF(is_read IS NULL,1,0) ) unread FROM message_users WHERE user_id=1
In terms of optimised, I'm not aware of a query that can necessarily go quicker than this. (Would love to know of it if it does exist!) There may be ways to speed things up if you have a problem with performance:
EXPLAIN
and some reading around etc.unread_messages
against user
and grab this value directly. Obviously there will need to be some on-write invalidation, or indeed some service running to keep these values up to date. There are many ways of achieving this, tools in MySQL, hand roll your own etc etc.Upvotes: 2
Reputation: 4453
You can use sum with CASE WHEN
clause. If is_read
is null then +1
is added to the sum, else +0
.
SELECT count(*),
SUM(CASE WHEN is_read IS NULL THEN 1 ELSE 0 END) AS count_unread
FROM message_users WHERE user_id = 1;
Upvotes: 1
Reputation: 9010
In MySQL, when you count a field, it only counts non null occurrences of that field, so you should be able to do something like this:
SELECT COUNT(user_id), COUNT(user_id) - COUNT(is_read) AS unread
FROM message_users
WHERE user_id = 1;
Untested, but it should point you in the right direction.
Upvotes: 1