Question

I have a table with like below.

+------+----------+--------+
|  id  |sender_id |receiver_id|
+------+----------+--------+
|  1   | 1        | 4      |
|  2   | 1        | 34     |
|  3   | 4        | 1      |
|  4   | 11       | 8      |
|  5   | 24       | 4      |
|  6   | 11       | 5      |
+------+----------+--------+

I want to to see my result like bellow.

+------+----------+--------+
|user_id|sent     |receive  |
+------+----------+--------+
|  1   | 2        | 1      |
|  4   | 1        | 2      |
|  5   | 0        | 1      |
|  8   | 0        | 1      |
| 11   | 2        | 0      |
| 24   | 1        | 0      |
| 34   | 0        | 1      |
+------+----------+--------+

I want to show my result in a one table, with a column user_id(all unique sender_id & receiver_id) as user_id, sent (count(sender_id) as sent, count(receiver_id)) as receive. So I can get a result in a one table every user sent & receive message number. I am trying to self join query but don't get my expected result.

Was it helpful?

Solution

I would use this query:

SELECT user_id, SUM(sent), SUM(receive)
FROM (
  SELECT sender_id AS user_id, COUNT(*) AS sent, 0 AS receive
  FROM tablename
  GROUP BY sender_id
  UNION ALL     
  SELECT receiver_id AS user_id, 0 AS sent, COUNT(*) AS receive
  FROM tablename
  GROUP BY receiver_id
) s
GROUP BY user_id

Please see fiddle here.

OTHER TIPS

Assuming that you have a table of users, one (inefficient, but concise) way would be to use correlated subqueries:

SELECT user_id,
       (SELECT COUNT(*) FROM messages WHERE sender_id   = u.user_id) sent,
       (SELECT COUNT(*) FROM messages WHERE receiver_id = u.user_id) received
FROM   users u

Another way would be to JOIN the tables and perform the counting during aggregation:

SELECT   u.user_id,
         SUM(m.sender_id   = u.user_id) sent,
         SUM(m.receiver_id = u.user_id) received
FROM     users u JOIN messages m ON u.user_id IN (m.sender_id, m.receiver_id)
GROUP BY u.user_id

A third (and possibly the most efficient) way would be to first aggregate the table two ways and then join the results:

SELECT user_id, m1.sent, m2.received
FROM   users LEFT JOIN (
         SELECT   sender_id AS user_id, COUNT(*) AS sent
         FROM     messages
         GROUP BY user_id
       ) m1 USING (user_id) LEFT JOIN (
         SELECT   receiver_id AS user_id, COUNT(*) AS received
         FROM     messages
         GROUP BY user_id
       ) m2 USING (user_id)

However, if this is an operation that you expect to undertake frequently over a large dataset, you might instead consider cacheing the number of messages sent/received per user in the users table (and simply update that whenever a new message is sent).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top