2010-09-01 6 views
1

Je suis ici pour obtenir des enregistrements en fonction des catégories.Comment puis-je obtenir les enregistrements n premiers de chaque catégorie?

Ma table toto a des champs [id, nom, classe]. mes dossiers peuvent être comme:

1, ram, 10 
2, hari, 9 
3, sita, 10 
4, gita, 9 
5, rita, 5 
6, tina, 7 
8, nita, 8 
9, bita, 5 
10,seta, 7 

... et plus ...

Maintenant, je voudrais obtenir un résultat à chaque enregistrement de différentes classes .. à savoir quelque chose comme

1, ram, 10 
2, hari, 9 
5, rita, 5 
6, tina, 7 
8, nita, 8 

soit un peu haut dossiers comme par classe

Répondre

6

Pour SQL Server 2005+ et Oracle 9i +, utilisez les fonctions analytiques:

WITH summary AS (
    SELECT f.id, 
     f.name, 
     f.class, 
     ROW_NUMBER() OVER (PARTITION BY f.class 
           ORDER BY f.name) AS rank 
    FROM FOO f) 
SELECT s.id, 
     s.name, 
     s.class 
    FROM summary s 
WHERE s.rank = 1 

Il utilise également une expression de table commune (CTE), connu sous le nom des sous-requêtes Factoring dans Oracle ...

MySQL ne supporte pas la fonction analytique, vous devez utiliser:

SELECT x.id, 
     x.name, 
     x.class 
    FROM (SELECT f.id, 
       f.name, 
       f.class, 
       CASE 
       WHEN @class = f.class THEN @rownum := @rownum + 1 
       ELSE @rownum := 1 
       END AS rank, 
       @class := f.class 
      FROM FOO f 
      JOIN (SELECT @rownum := 0, @class := '') r 
     ORDER BY f.class, f.name) x 
WHERE x.rank = 1 
1

Avec SQL Server ou Oracle (ou tout autre moteur implémentant cette partie de la norme, y compris par exemple PostgreSQL parmi les gratuits), les "fonctions de fenêtre" dans une clause OVER (par exemple, voir here pour les documents MS à leur sujet) facilitent la tâche; Par exemple, dans this SO question, voir la réponse de @ Darrel (il sélectionne le top 10 par catégorie, vous ne voulez que le top 1, les changements devraient être évidents ;-). En MySql, ou tout autre moteur non conforme à la norme concernant la clause OVER, vous pouvez utiliser la réponse de @ Bill (bon pour MySql, pas pour les autres) ou @ Matt (peut nécessiter une légère adaptation puisqu'il répond à SQL Server et donc en utilisant SELECT TOP 10 ... - en MySql ce serait SELECT ... LIMIT 10! -).

3

J'ai testé en 2008 sql cela et travaille pour moi, j'espère que cela vous aide d'une certaine manière.

DECLARE @Class TABLE 
(
    id INT 
    ,Name NVARCHAR(120) 
    ,Class INT 

    PRIMARY KEY (id) 
) 

INSERT INTO @Class values (1, 'ram', 10) 
INSERT INTO @Class values (2, 'hari', 9) 
INSERT INTO @Class values (3, 'sita', 10) 
INSERT INTO @Class values (4, 'gita', 9) 
INSERT INTO @Class values (5, 'rita', 5) 
INSERT INTO @Class values (6, 'tina', 7) 
INSERT INTO @Class values (8, 'nita', 8) 
INSERT INTO @Class values (9, 'bita', 5) 
INSERT INTO @Class values (10, 'seta', 7) 

SELECT A.id, A.Name, A.Class 
FROM 
(
    SELECT ROW_NUMBER() OVER (PARTITION BY Class ORDER BY ID) as Num, ID, Name, Class 
    FROM @Class 
) A 
WHERE A.Num = 1 
ORDER BY id 
+0

fonctionne-t-il pour SQL 2000 également – KoolKabin

+0

Non, il est de 2005 sql +. Vous en avez besoin pour SQL 2000? –

4

Cela devrait être le moyen le plus simple, qui ne comporte pas d'options spécifiques à la base:

select * 
    from foo 
where id in (select min(id) 
       from foo 
       group by class); 

màj: ouais, bien sûr, cela ne peut fonctionner que si vous avez besoin qu'un seul enregistrement de chaque classe. Upd2: juste pour le plaisir de proposer une requête qui vous montre TOP N et n'implique pas d'analyse. semble un peu désordonné, mais semble fonctionner :)

select newfoo.id, newfoo.name, newfoo.class 
    from (select class, max(r) top, min(r) bottom 
      from (select f.*, rownum r 
        from (select id, name, class from foo order by class, id asc) f) 
     group by class) minmax, 
     (select id, name, class, r 
      from (select f.*, rownum r 
        from (select id, name, class from foo order by class, id asc) f)) newfoo 
where newfoo.class = minmax.class 
    and newfoo.r between minmax.bottom and 
     least(minmax.bottom + (TOP_N-1), minmax.top); 

TOP_N est quantité de documents que vous devez obtenir.

+0

hmm ... assez intéressant et bon ... pour un seul enregistrement. Peut-il être modifié pour les n premiers enregistrements – KoolKabin

0

Voici une autre façon

DECLARE @foo TABLE(ID INT,Name VARCHAR(20),Class INT) 
INSERT INTO @foo 
SELECT 1,'ram', 10 UNION ALL 
SELECT 2, 'hari', 9 UNION ALL 
SELECT 3, 'sita', 10 UNION ALL 
SELECT 4, 'gita', 9 UNION ALL 
SELECT 5, 'rita', 5 UNION ALL 
SELECT 6, 'tina', 7 UNION ALL 
SELECT 8, 'nita', 8 UNION ALL 
SELECT 9, 'bita', 5 UNION ALL 
SELECT 10,'seta', 7 

SELECT DISTINCT X.* 
FROM @foo f 
CROSS APPLY(SELECT TOP 1 * FROM @foo WHERE Class = f.Class) AS X 
Questions connexes