2009-06-04 7 views
4

Mettre ce aussi simple que je peux, je la structure de tableau suivant:MySQL: Comment obtenir n dernières lignes d'un type distinct

Date | Type | Title

Say Type est une valeur comprise entre 1-10 , J'ai des milliers d'enregistrements dans la table, et je veux les 5 enregistrements les plus récents de type unique. Ainsi, le résultat serait quelque chose comme:

   Date | Type | Title 
2009-06-04 14:32:00 | 4 | Zeppo 
2009-06-04 14:31:00 | 2 | Groucho 
2009-06-04 14:30:00 | 8 | Harpo 
2009-06-04 14:29:00 | 5 | Gummo 
2009-06-04 14:28:00 | 3 | Chico 

On dirait que je veux soit DISTINCT à appliquer uniquement à la colonne Type, ou je veux un GROUP BY qui s'appliquera après une clause ORDER BY.

Tous MySQL 4.

+0

Est-ce que Gummo est supposé avoir '2' pour son type? –

+0

Euh, bon point - corrigé! –

Répondre

2

n'a pas testé dans des MySQL 4, mais MySQL 5 cela peut être facilement fait.

Pour que cela fonctionne, vous aurez besoin d'un PRIMARY KEY dans votre tableau.

SELECT l.* 
FROM (
     SELECT type, 
       COALESCE(
       (
       SELECT id 
       FROM mytable li 
       WHERE li.type= dlo.type 
       ORDER BY 
         li.type DESC, li.date DESC, li.id DESC 
       LIMIT 4, 1 
       ), CAST(0xFFFFFFFF AS DECIMAL)) AS mid 
       COALESCE(
       (
       SELECT date 
       FROM mytable li 
       WHERE li.type= dlo.type 
       ORDER BY 
         li.type DESC, li.date DESC, li.id DESC 
       LIMIT 4, 1 
       ), '9999-31-12') AS mdate 
     FROM (
       SELECT DISTINCT type 
       FROM t_mytable dl 
       ) dlo 
     ) lo, t_mytable l 
WHERE l.type >= lo.type 
     AND l.type <= lo.type 
     AND (l.date, l.id) >= (lo.mdate, lo.mid) 

Voir cette entrée dans mon blog pour plus de détails sur la façon dont cela fonctionne:

Si vous ne pouvez pas ajouter un PRIMARY KEY à mettre en œuvre cette solution, vous pouvez essayer d'utiliser une moins efficace en utilisant des variables du système:

SELECT l.* 
FROM (
     SELECT @lim := 5, 
       @cg := -1 
     ) vars, 
     mytable l 
WHERE CASE WHEN @cg <> type THEN @r := @lim ELSE 1 END > 0 
     AND (@r := @r - 1) >= 0 
     AND (@cg := type) IS NOT NULL 
ORDER BY 
     type DESC, date DESC 

Il est décrit ici:

Mise à jour:

Si vous ne souhaitez pas sélectionner 5 enregistrements pour chaque type (qui donnerait 5 x number of types enregistrements dans l'ensemble de résultats), mais que vous voulez au lieu de sélectionner 5 derniers enregistrements de type distinct (qui donnerait 5 enregistrements dans l'ensemble de résultats), utilisez cette requête:

SELECT date, type, title 
FROM mytable m 
WHERE NOT EXISTS 
     (
     SELECT 1 
     FROM mytable mi 
     WHERE mi.date > m.date 
       AND mi.type = m.type 
     ) 
ORDER BY 
     date DESC 
LIMIT 5 

Si vous avez beaucoup de types, ce sera plus efficace que l'utilisation GROUP BY, à condition que vous avez un index sur date.

+0

Grands articles. Je pense que vous pouvez simplement dire à MySQL quel index utiliser, au lieu de votre truc au bas de l'échantillonnage de lignes avancées: http://dev.mysql.com/doc/refman/5.1/fr/index-hints.html –

+0

@Adam: l'index sera utilisé, mais seulement pour le filtrage grossier sur la première colonne avec l'utilisation de WHERE sur le second. Croyez-moi, chaque ligne de cette requête est exprès :) – Quassnoi

1

En supposant les performances des requêtes est assez important et que vous êtes coincé avec MySQL 4 j'aller avec quelque chose comme ceci:

SELECT Date, Type, Title 
FROM (
    SELECT Date, Type, Title 
    FROM Table 
    WHERE Type = 1 
    ORDER BY Date 
    LIMIT 1 

    UNION ALL 

    SELECT Date, Type, Title 
    FROM Table 
    WHERE Type = 2 
    ORDER BY Date 
    LIMIT 1 

    ... 
) x 
ORDER BY Date 
LIMIT 5 

Il est pas joli, mais il doit faire le travail et rapidement. Si vous ajoutez régulièrement de nouvelles valeurs à la colonne Types, cela peut ne pas être pour vous, car cela nécessiterait de modifier régulièrement la requête.

Je pense que les tables dérivées ont été ajoutées en 4.1, donc vous aurez besoin d'être au moins aussi élevé dans la série 4. Si vous étiez sur la version 5 ou sur une base de données différente, il y aurait de meilleures façons d'aborder cela.

+0

Il est 4.1, donc je pourrais regarder dans les tables dérivées. Malheureusement, la valeur du type changera assez fréquemment pour rendre cette approche un peu pénible en termes de maintenance, mais c'est une approche intéressante - merci. –

1

Cela semble être un bon cas pour l'instruction union et les sélections imbriquées.

Quelque chose le long des lignes de:

select t.date, t.type, t.title from (Select top 5 table.Date, table.Type, table.Title from table where type = 1 order by table.date desc) as t 

union 

select t2.date, t2.type, t2.title from (Select top 5 table.Date, table.Type, table.Title from table where type = 2 order by table.date desc) as t2 

[...] 

select t10.date, t10.type, t10.title from (Select top 5 table.Date, table.Type, table.Title from table where type = 10 order by table.date desc) as t10 

order by type, date 

Malheureusement, je ne dispose que sql server 2008 pour tester ce ici, donc YMMV; mais c'est un truc assez basique et ça devrait fonctionner car les sélections imbriquées et les unions sont supportées dans MySQL 4.0 selon le guide de référence. (J'ai ajouté la clause as à l'instruction imbriquée à partir de la documentation mysql.)

6

Ai-je raté quelque chose? La solution facile semble être:

SELECT MAX(date) AS max_date, type, title 
FROM table 
GROUP BY 
     type 
ORDER BY 
     max_date DESC 
LIMIT 5 

Et il devrait être extrêmement rapide.

+0

@Adam: Je pense que votre solution est ce que l'auteur signifie vraiment, mais il ne renvoie pas "5 enregistrements les plus récents de chaque type" comme il _says_. – Quassnoi

+0

@Adam: mais je pense que c'est un +1 de toute façon. – Quassnoi

0

Essayez cette

select 
    T.* 
from 
     (select 
      max(date) dt, 
      Type 
     from 
      table 
     group by type 
     order by dt desc 
     limit 5) subQuery 
     inner join table T on T.date = subQuery.dt and T.Type = subQuery.Type 

Je n'ai essayé sur MySQL 5. Cela suppose le champ de date est unique. Si vous avez une clé primaire autre que la date, utilisez-la dans la requête.

0

Je sais qu'il a été un peu en retard, mais a eu le même problème et a compris comment: alors je publie.

SELECT t.Date, t.Type, t.Title 
FROM (SELECT Date, Type, Title FROM table ORDER BY Date DESC) AS t 
GROUP BY t.Type LIMIT 5; 

Je ne pense pas qu'il existe une solution plus simple.

Explication

  1. Le SELECT interne est exécuté en premier. Cela renvoie une table avec lignes, DESC triés sur la colonne Date. Dans ce tableau, la dernière entrée pour un type particulier est avant tout les autres entrées du même type. Cette table de retour est renommée t.

  2. Maintenant, une opération GROUP BY est effectuée sur cette table en fonction de la colonne Type. Le résultat serait la première ligne rencontrée, de chaque type.

  3. Ajoutez maintenant une condition LIMIT à pour obtenir les 5 premières lignes.

Questions connexes