2009-01-20 4 views
19

Si j'ai une table USER commeSQL sélectionnez nième membre du groupe

class | age 
-------------- 
1  20  
3  56 
2  11 
1  12 
2  20 

Ensuite, je peux facilement obtenir le plus jeune utilisateur dans chaque classe via

select class, min(age) 
from user 
group by class; 

De même, en remplaçant min avec max, Je peux avoir le plus vieux. Mais comment puis-je obtenir le 10e plus jeune (ou plus ancien) dans chaque classe? En passant, j'utilise MySQL v.5.0.

Cheers,

Répondre

0

Il est assez facile dans SQL Server:

select 
    * 
from(
    select 
     *, 
     row_number() over(order by age asc) as eldest 
    from class order by age asc) a 
where a.eldest = 10 

Après ce modèle, pour MySQL, je pense que vous voudriez regarder ceci: http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/

+0

La méthode dans le lien ne fonctionne pas mais il est un peu un hack. C'est aussi très rapide. Pour un offs bien, pour la production non! –

11
SELECT a.class, 
(
    SELECT b.age 
    FROM users b 
    WHERE b.class = a.class 
    ORDER BY age 
    LIMIT 1,1 
) as age 
FROM users a 
GROUP BY a.class 

Aurait le 2ème plus jeune dans chaque classe. Si vous vouliez le 10ème plus jeune, vous feriez LIMIT 9,1 et si vous vouliez le 10ème plus ancien, vous feriez ORDER BY age DESC.

+1

Cela me donnera le 10ème plus vieux total, pas le 10ème plus vieux dans chaque classe. –

+0

Oups, n'a pas remarqué qu'il voulait regroupés. Correction ... –

+0

Cela ne semble pas fonctionner pour MySQL v5.5: ERROR 1235 (42000): Cette version de MySQL ne supporte pas encore 'LIMIT & IN/ALL/ANY/SOME sous-requête' – Marcus

0
SELECT 
    userid, 
    class, 
    age, 
    ( SELECT COUNT(1) FROM user 
      WHERE class = c.class AND age > u.age 
    ) AS oldercount 
FROM user AS u 
WHERE oldercount = 9 
GROUP BY class 

ou

SELECT userid, 
     class, 
     age 
    FROM user AS u  
    WHERE (SELECT COUNT(1) FROM class WHERE class = c.class AND age > u.age) = 9 
GROUP BY class 
+0

besoin d'une clé unique si deux personnes avaient le même âge et que vous vouliez seulement en retourner une. –

+0

La première requête provoque l'erreur: "La colonne inconnue" plus jeune "dans la clause" where "" –

+0

Les deux rescanner la classe entière pour chaque enregistrement dans la table source. Devrait être faisable sans frapper une loi carrée ... – MatBailie

1

La seule manière indépendante sql (même si vous n'avez pas sous-requêtes MySQL < 5)

select u1.class, u1.age, count(*) from  user u1 join user u2 
on u1.class = u2.class and u1.age >= u2.age 
group by u1.class, u1.age 
having count(*) = [number] 

vous obtient le plus ancien par [numéro] classe

select u1.class, u1.age, count(*) from  user u1 join user u2 
on u1.class = u2.class and u1.age <= u2.age 
group by u1.class, u1.age 
having count(*) = [number] 

vous obtient le [nombre] le plus jeune par classe

Si deux personnes ont le même âge, cela peut ne pas fonctionner car les deux sont retournés. Si vous voulez seulement retourner l'un d'entre eux, vous aurez besoin d'une clé unique et la requête est plus compliquée.

0

Toute réponse qui se joint à une table sur elle-même va créer une loi carrée ...

- a JOIN b ON a.class = b.class AND a.age >= b.age 
- on average the >= condition will be true for half the class 

- 6 people in a class 
->6*6/2 = 18 

- 10 people in a class 
->10*10/2 = 50 

-> very rapid growth 

Comme les tailles de table poussent la performance se dégrade rapidement. Si vous gardez les choses petites et qu'elles ne grandiront pas beaucoup, est-ce un problème? Votre appel ... il

Une alternative implique plus de code, mais croît linéairement ...

  • Tout d'abord, insérez tous les enregistrements intoa nouvelle table, avec un champ d'identité, classés par ordre de classe alors l'âge
  • maintenant, pour chaque classe, trouver le MIN (id)
  • maintenant, pour chaque classe, RINF le dossier où est = MIN (id) + 8 (pour le 9 aîné)

Il y a beaucoup des façons de faire les 2 dernières étapes. J'utiliserais personnellement ...

SELECT 
    [USER_WITH_IDS].id, 
    [USER_WITH_IDS].class, 
    [USER_WITH_IDS].age 
FROM 
    [USER_WITH_IDS] 
WHERE 
    [USER_WITH_IDS].id = (
          SELECT 
           MIN([min].ID) + 8 
          FROM 
           [USER_WITH_IDS] AS [min] 
          WHERE 
           [min].class = [USER_WITH_IDS].class 
         ) 

Ce que cela donne est ...

  • Un laissez-passer pour créer les nouveaux ID
  • Un laissez-passer pour obtenir le MIN (id) pour chaque classe
  • Un laissez-passer pour obtenir les dossiers dont vous avez besoin

  • Et selon la façon dont bon l'optimiseur est, en utilisant un index (classe puis id) lui permettrait de combiner les 2 dernières passes en 1 passe.

2 ou 3 passes, quelle que soit la taille de la table ou de la classe. Linéaire, pas la loi carrée ...

7

Ici N présente Nth enregistrement oldest

SELECT * 
FROM users k 
WHERE N = (SELECT 
      COUNT(DISTINCT age) 
      FROM users u 
      WHERE k.age >= u.age 
       AND k.class = u.class 
      GROUP BY u.class) 

et donne Nth enregistrement youngest

SELECT * 
FROM users k 
WHERE N = (SELECT 
      COUNT(DISTINCT age) 
      FROM users u 
      WHERE k.age <= u.age 
       AND k.class = u.class 
      GROUP BY u.class) 
+0

Vous économisez ma journée, merci !!!!!! – mzalazar

+0

génial !! ceci devrait être marqué comme réponse –