2010-09-12 4 views
4

I ont une table de messages qui ressemble à ceci:postgres par fonction d'agrégation

+------------+-------------+----------+ 
| sender_id | created_at | message | 
+------------+-------------+----------+ 
|  1  | 2010-06-14 | the msg | 
|  1  | 2010-06-15 | the msg | 
|  2  | 2010-06-16 | the msg | 
|  3  | 2010-06-14 | the msg | 
+------------+-------------+----------| 

Je veux sélectionner le message le plus récent pour chaque expéditeur.

Cela ressemble à un GROUP BY sender_id et à ORDER BY created_at mais j'ai du mal à obtenir le message le plus récent sélectionné.

J'utilise postgres donc besoin d'une fonction d'agrégation sur le terrain created_at dans l'instruction SELECT si je veux commander par ce champ si je cherchais à faire quelque chose comme cela comme un test initial

SELECT messages.sender_id, MAX(messages.created_at) as the_date 
FROM messages 
GROUP BY sender_id 
ORDER BY the_date DESC 
LIMIT 10; 

Cela semble fonctionner mais quand je veux aussi sélectionner 'message', je n'ai aucune idée de la fonction d'agrégat à utiliser. Je veux simplement juste le message qui correspond au MAX created_at.

Y a-t-il un moyen d'y arriver ou est-ce que je m'approche de la mauvaise façon?

+0

Quelle version êtes-vous? –

+0

8.3 Je crois (heroku) – johnnymire

Répondre

6

Ce:

SELECT * 
FROM (
     SELECT DISTINCT ON (sender_id) * 
     FROM messages 
     ORDER BY 
       sender_id, created_at DESC 
     ) q 
ORDER BY 
     created_at DESC 
LIMIT 5 

ou ceci:

SELECT (mi).* 
FROM (
     SELECT (
       SELECT mi 
       FROM messages mi 
       WHERE mi.sender_id = m.sender_id 
       ORDER BY 
         created_at DESC 
       LIMIT 1 
       ) AS mi 
     FROM messages m 
     GROUP BY 
       sender_id 
     ) q 
ORDER BY 
     (mi).created_at DESC 
LIMIT 5 

Créer un pour cet indice sur (sender_id, created_at) travailler rapidement.

Vous pouvez trouver cet article intéressant:

+0

Celui-ci donne un résultat différent, l'ordre de tri est différent. –

+0

@Frank: vous pouvez utiliser ceci comme une sous-requête et réorganiser les résultats avec votre requête supérieure. – Kevin

+0

@Frank: la question d'origine n'a pas mentionné l'ordre sur 'sender_id', mais voici la requête avec l'ordre corrigé. – Quassnoi

1

Utilisez une sous requête corrélative:

select * from messages m1 
where m1.created_at = (
    select max(m2.create_at) 
    from messages m2 
    where m1.sender_id = m2.sender_id 
); 

La sous requête est réévaluées pour chaque ligne traitée par la requête supérieure .

+0

Donc, si vous le savez, pourquoi le suggérez-vous? C'est une idée horrible ... –

0

Utilisation distincte sur:

SELECT DISTINCT ON (sender_id) 
      sender_id,created_at,message 
     FROM messages 
    ORDER BY sender_id,created_at DESC