99 votes

Grouper par valeur minimale dans un champ tout en sélectionnant des lignes distinctes

Voici ce que j'essaie de faire. Disons que j'ai ce tableau t :

key_id | id | record_date | other_cols
1      | 18 | 2011-04-03  | x
2      | 18 | 2012-05-19  | y
3      | 18 | 2012-08-09  | z
4      | 19 | 2009-06-01  | a
5      | 19 | 2011-04-03  | b
6      | 19 | 2011-10-25  | c
7      | 19 | 2012-08-09  | d

Pour chaque identifiant, je veux sélectionner la ligne contenant la date d'enregistrement minimale. J'obtiendrais donc

key_id | id | record_date | other_cols
1      | 18 | 2011-04-03  | x
4      | 19 | 2009-06-01  | a

Les seules solutions que j'ai vues à ce problème supposent que toutes les entrées record_date sont distinctes, mais ce n'est pas le cas dans mes données. L'utilisation d'une sous-requête et d'une jointure interne avec deux conditions me donnerait des lignes en double pour certains identifiants, ce que je ne veux pas :

key_id | id | record_date | other_cols
1      | 18 | 2011-04-03  | x
5      | 19 | 2011-04-03  | b
4      | 19 | 2009-06-01  | a

2voto

epox_spb Points 281

Si record_date n'a pas de doublons au sein d'un groupe :

pensez-y comme à un filtrage. Il suffit d'obtenir (CONSIDERANT) un ( MIN(record_date) ) du groupe actuel :

SELECT * FROM t t1 WHERE record_date = (
                                 select MIN(record_date)
                                 from t t2 where t2.group_id = t1.group_id)

S'il pouvait y avoir 2+ min record_date au sein d'un groupe :

  1. filtrer les lignes non min (voir ci-dessus)

  2. puis (ET) en choisir un seul parmi les 2+ min record_date rangées, dans le cadre de la group_id . Par exemple, choisissez celui qui a la clé la moins unique :

                    AND key_id = (select MIN(key_id)
                                  from t t3 where t3.record_date = t1.record_date
                                              and t3.group_id    = t1.group_id)

donc

key_id | group_id | record_date | other_cols
1      | 18       | 2011-04-03  | x
4      | 19       | 2009-06-01  | a
8      | 19       | 2009-06-01  | e

sélectionnera key_id s : # 1 et # 4

1voto

SELECT p.* FROM tbl p
INNER JOIN(
  SELECT t.id, MIN(record_date) AS MinDate
  FROM tbl t
  GROUP BY t.id
) t ON p.id = t.id AND p.record_date = t.MinDate
GROUP BY p.id

Ce code élimine les doublons record_date au cas où il y en aurait d'autres ids avec le même record_date .
Si vous voulez des doublons, supprimez la dernière ligne GROUP BY p.id .

0voto

Luciano Marqueto Points 694

C'est une vieille question, mais elle peut être utile à quelqu'un. Dans mon cas, je ne peux pas utiliser une sous-requête car j'ai une grosse requête et j'ai besoin d'utiliser min() sur mon résultat, si j'utilise une sous-requête, la base de données doit réexécuter ma grosse requête.

select t.* 
    from (select m.*, @g := 0
        from MyTable m --here i have a big query
        order by id, record_date) t
    where (1 = case when @g = 0 or @g <> id then 1 else  0 end )
          and (@g := id) IS NOT NULL

En fait, j'ai ordonné les résultats, puis j'ai placé une variable afin de n'obtenir que le premier enregistrement de chaque groupe.

0voto

SRVFan Points 184

La requête ci-dessous prend la première date pour chaque bon de travail (dans un tableau montrant tous les changements d'état) :

SELECT
    WORKORDERNUM,
    MIN(DATE)
FROM
    WORKORDERS
WHERE
    DATE >= to_date('2015-01-01','YYYY-MM-DD')
GROUP BY
    WORKORDERNUM

0voto

user2867940 Points 1
select 
    department, 
    min_salary, 
    (select s1.last_name from staff s1 where s1.salary=s3.min_salary ) lastname 
from 
    (select department, min (salary) min_salary from staff s2 group by s2.department) s3

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X