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).