tags:

views:

128

answers:

6
+2  Q: 

Wrestling with SQL

I have this in a MySQL db:

table Message
    sender_id int
    recipient_id int
    created datetime
    [title, body, etc... omitted]

Is it possible to get from this, in a single query, a list of all the users who have been communicating with a given user with id N (meaning N appears in either sender_id or recipient_id), ordered by created, including the most recent value of created, without duplicates (no sender/recipient pairs N, M and M, N)?

I'm pretty sure the answer is No, but I'm having trouble convincing myself. Afternoon mental dullness.

If No, what's the most efficient alternative you would recommend?


EDIT: holy crap, Stack Overflow is hopping these days. 8 answers before I realize I forgot to specify that I would like to have the most-recent value of created present along with the other user ID in each row.

+1  A: 

No, it's pretty simple to do.

SELECT DISTINCT IF(sender_id=4, recipient_id, sender_id) AS partner_id
FROM message
WHERE sender_id=4 OR recipient_id=4
Franz
This way you don't need an UNION.Obviously you have to replace 4 with your users' ID.
Franz
ah, this is close. unfortunately i was unclear and i also need the timestamp itself, which breaks distinct, i think?
lawrence
Why would it break the DISTINCT?
Franz
One more thing, though: DISTINCT is usually pretty slow. Usually GROUP BY is faster, but I'm not sure how to apply that in this case. Maybe somebody else can help with that...
Franz
because if i do "select distinct IF(sender_id=4, recipient_id, sender_id) AS partner_id, created" then i get duplicate partner_ids
lawrence
Ah right. That means we have to use GROUP BY. Let me check that.
Franz
A: 

Obviously you're having trouble convincing yourself, otherwise you wouldn't have asked. And I am pretty sure the answer is Yes. Yet, without knowing some particularities about the underlying data, it's a bit hard to find the right solution.

It probably goes into the direction of

select 
  sender_id,
  recipient_id
from
  message
where
 (sender_id    = n or 
  recipient_id = n   )
order by
  created desc
René Nyffenegger
+4  A: 

Try this

SELECT distinct recipient_id
FROM Message
WHERE sender_id = @id

UNION

SELECT distinct sender_id
FROM Message
WHERE recipient_id = @id

The union clause will remove duplicates across the two queries. Actually looking at it you won't need the distinct's either as the union will do that for you too (anyone any idea if it's more efficient to use distinct to pre-filter the clauses or just let the union handle all the duplication?)

Cruachan
need to `ORDER BY created`
Russ Cam
Myself I'd just make a view then use that to join back to the tables for any additional fields
Cruachan
I guess UNION is probably faster, since DISTINCT is pretty slow. Also, only using DISTINCT would still allow duplicates, when communication with another person happens both through sending and receiving...
Franz
You don't need the `DISTINCT` if you're going to be using `UNION` - you would if you were using `UNION ALL`.
OMG Ponies
Unless there are tens of thousands of rows involved then there's not going to be a big efficiency hit and having a view of distinct ids available for use elsewhere is nicely modular and easy to understand
Cruachan
In fact, we're asked to include the most recent created date per user => you'll have to include group by clauses :)
Manitra Andriamitondra
I don't like Unions. They are very slow on some dbms.
Stefan Steinegger
@manitra: Correct, see my answer.
OMG Ponies
heh, I posted my original answer before he added the rider about created. But I still think using this as a view and joining back is simpler to understand for ongoing maintenance purposes
Cruachan
@Cruachan: Been there, loosing out to changing requirements :/
OMG Ponies
+3  A: 

Use:

SELECT x.friend,
       MAX(x.created)
  FROM (SELECT t.recipient_id 'friend',
               t.created
          FROM MESSAGE t
         WHERE t.sender_id = @id 
        UNION
        SELECT r.sender_id 'friend',
               r.created
          FROM MESSAGE r
         WHERE r.recipient_id = @id) x
GROUP BY x.friend
OMG Ponies
i had just finished writing up my own version of this from your original posting, but i deleted it since you should get the credit
lawrence
A: 

I think this will meet your requirements most:

SELECT *
FROM user
WHERE 
  user_id in 
    (select recipient_id FROM message m where m.sender_id = X)
  OR user_id in
    (select sender_id FROM message m where m.recipient_id = X)
order by
  created DESC

There are no duplicates, no unions and no SQL dialect specific stuff.

Stefan Steinegger
+1  A: 

Hi,

Here is mine :)

select id, max(created) as lastMessage
(
    select sender_id as id, created
    from Message
    union
    select recipient_id as id, created
    from Message
) as MergedMessage
where id = ?id
group by id
order by max(created) desc

That's it :

  • without duplicates
  • order by created desc
Manitra Andriamitondra