1
votes

Comment trouver le min et le max de chaque groupe avec SQL?

J'essaie de comprendre comment je pourrais faire cela en SQL. J'ai une table avec les colonnes suivantes dans la table Client - (Customer_Id, Gender, Birthday). La question est - je dois trouver le premier et le dernier né, par sexe. Essentiellement Min et Max par différents groupes.

    select a.Customer_id, a.gender, b.min_birthday
    from(
    select gender, min(birthday) min_birthday
    from Sales..Customer group by gender) b join Sales..Customer a on b.gender = a.gender 
    and b.min_birthday = a.birthday

L'ensemble de résultats devrait ressembler à ceci

188     M       2008-09-01 00:00:00.000
123     M       2017-07-05 00:00:00.000
111     F       2000-01-01 00:00:00.000
567     F       2015-02-07 00:00:00.000

Je pourrais faire 4 UNION S et le comprendre de cette façon, mais ce serait inefficace.

Voici ce que j'ai trouvé mais cela ne fonctionnera pas non plus. Comment faire pour MAX groupes également dans une seule requête?

123 M   2017-07-05 00:00:00.000
345 M   2016-08-01 00:00:00.000
555 F   2012-01-09 00:00:00.000
567 F   2015-02-07 00:00:00.000
789 F   2013-01-02 00:00:00.000
111 F   2000-01-01 00:00:00.000
188 M   2008-09-01 00:00:00.000


0 commentaires

3 Réponses :


1
votes

Une méthode utilise des fonctions de fenêtre:

select c.*
from ((select top (1) c.*
       from customer c
       where gender = 'M'
       order by birthday
      ) union all
      (select top (1) c.*
       from customer c
       where gender = 'F'
       order by birthday
      ) union all
      (select top (1) c.*
       from customer c
       where gender = 'M'
       order by birthday desc
      ) union all
      (select top (1) c.*
       from customer c
       where gender = 'F'
       order by birthday desc
      )
     ) c;

Utilisez rank () au lieu de row_number () si vous voulez des liens. p>

Cela dit, avec un index sur (sexe, anniversaire) et (sexe, anniversaire desc) (les deux index peuvent ne plus être nécessaires si l'optimiseur a amélioré), l'approche union all devrait très bien fonctionner:

select customer_id, gender, birthday
from (select c.*,
             row_number() over (partition by gender order by birthday) as seqnum_asc,
             row_number() over (partition by gender order by birthday desc) as seqnum_desc
      from customer c
     ) c
where 1 in (seqnum_asc, seqnum_desc);


2 commentaires

Gordon, merci. La fonction de classement ne me donnera que des anniversaires minimum pour les deux groupes de sexe (car vous ne récupérez que le premier rang). Comment récupérer le nombre maximum d'anniversaires, étant donné que leur classement sera toujours dynamique?


@peppa. . . C'était une faute de frappe. J'ai mis desc dans le nom, mais pas dans la clause order by .



0
votes

En fait, un UNION ALL suffit :)

select gender, min(birthday), max(birthday)
from Sales..Customer group by gender

Mais une meilleure performance serait avec:

select gender, min(birthday) birthday, 'MIN' Aggregate
from Sales..Customer group by gender
union all
select gender, max(birthday), 'MAX'
from Sales..Customer group by gender

Mais le résultat sera légèrement différent de celui souhaité.


0 commentaires

0
votes

Vous pouvez le faire avec NOT EXISTS:

> id  | gender | birthday           
> :-- | :----- | :------------------
> 111 | F      | 01/01/2000 00:00:00
> 567 | F      | 07/02/2015 00:00:00
> 188 | M      | 01/09/2008 00:00:00
> 123 | M      | 05/07/2017 00:00:00

Voir le démo .
Résultats:

select c.* from Customer c
where not exists (
  select 1 from Customer
  where gender = c.gender and birthday < c.birthday
) or not exists (
  select 1 from Customer
  where gender = c.gender and birthday > c.birthday
)
order by c.gender, c.birthday


0 commentaires