web-dev-qa-db-fra.com

Sélectionnez les 3 enregistrements les plus récents où les valeurs d'une colonne sont distinctes

J'ai le tableau suivant:

    id       time      text      otheridentifier
    -------------------------------------------
    1        6         Apple     4
    2        7         orange    4
    3        8         banana    3
    4        9         pear      3
    5        10        grape     2

Ce que je veux faire est de sélectionner les 3 enregistrements les plus récents (par heure de desc), dont les otheridentifiers sont distincts. Donc, dans ce cas, le résultat serait id's: 5, 4 et 2.

id = 3 serait ignoré car il existe un enregistrement plus récent avec le même champ otheridentifier.

Voici ce que j'ai essayé de faire:

SELECT * FROM `table` GROUP BY (`otheridentifier`) ORDER BY `time` DESC LIMIT 3

Cependant, je finis par obtenir des rangées de id = 5, 3 et 1 au lieu de 5, 4, 2 comme prévu. 

Quelqu'un peut-il me dire pourquoi cette requête ne renvoie pas ce que j'attendais? J'ai essayé de changer ORDER BY en ASC, mais cela a simplement réorganisé les lignes retournées en 1, 3, 5.

45
atp

Il ne renvoie pas ce que vous attendez, car le regroupement a lieu avant la commande, comme l'indique la position des clauses dans l'instruction SQL. Malheureusement, vous allez devoir être plus chic pour obtenir les rangs que vous voulez. Essaye ça:

SELECT *
FROM `table`
WHERE `id` = (
    SELECT `id`
    FROM `table` as `alt`
    WHERE `alt`.`otheridentifier` = `table`.`otheridentifier`
    ORDER BY `time` DESC
    LIMIT 1
)
ORDER BY `time` DESC
LIMIT 3
34
chaos

Vous pouvez rejoindre la table sur elle-même pour filtrer la dernière entrée par otheridentifier, puis prendre les 3 premières lignes de celle-ci:

SELECT last.*
FROM `table` last
LEFT JOIN `table` prev 
    ON prev.`otheridentifier` = last.`otheridentifier`
    AND prev.`time` < last.`time`
WHERE prev.`id` is null
ORDER BY last.`time` DESC 
LIMIT 3
18
Andomar

J'avais une exigence similaire, mais j'avais des critères de sélection plus avancés. En utilisant certaines des autres réponses, je ne pouvais pas obtenir exactement ce dont j'avais besoin, mais j’ai trouvé que vous pouvez toujours faire un GROUP BY après et ORDER BY comme ceci:

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t 
GROUP BY t.otheridentifier
4
11101101b
SELECT * FROM table t1 
WHERE t1.time = 
    (SELECT MAX(time) FROM table t2 
     WHERE t2.otheridentifier = t1.otheridentifier)
2
Rytmis

La réponse d'Andomar est probablement meilleure car elle n'utilise pas de sous-requête.

Une approche alternative: 

select *
from   `table` t1
where  t1.`time` in (
                    select   max(s2.`time`)
                    from     `table` t2
                    group by t2.otheridentifier
                    )
2
bernie

Vous pouvez utiliser cette requête pour obtenir la réponse correcte:

SELECT * FROM 
      (SELECT * FROM `table` order by time DESC)
          t group by otheridentifier
2
php

qu'en est-il de 

SELECT *, max(time) FROM `table`  group by otheridentifier
1
mbo

Ça aussi:

SELECT * FROM
OrigTable T INNER JOIN 
( 
SELECT otheridentifier,max(time) AS duration
FROM T
GROUP BY otheridentifier) S
ON S.duration = T.time AND S.otheridentifier = T.otheridentifier.
0
user2450223