web-dev-qa-db-fra.com

Sélectionner les 10 meilleurs enregistrements pour chaque catégorie

Je souhaite renvoyer les 10 meilleurs enregistrements de chaque section dans une requête. Quelqu'un peut-il aider avec comment le faire? Section est l'une des colonnes du tableau.

La base de données est SQL Server 2005. Je veux renvoyer le top 10 par date saisie. Les sections sont business, local et feature. Pour une date particulière, je souhaite uniquement les 10 premières lignes métier (entrée la plus récente), les 10 premières lignes locales et les 10 premières fonctionnalités.

188
jbcedge

Si vous utilisez SQL 2005, vous pouvez faire quelque chose comme ça ...

SELECT rs.Field1,rs.Field2 
    FROM (
        SELECT Field1,Field2, Rank() 
          over (Partition BY Section
                ORDER BY RankCriteria DESC ) AS Rank
        FROM table
        ) rs WHERE Rank <= 10

Si votre RankCriteria a des liens, vous pouvez renvoyer plus de 10 lignes et la solution de Matt peut être meilleure pour vous.

197
Darrel Miller

Dans T-SQL, je ferais:

WITH TOPTEN AS (
    SELECT *, ROW_NUMBER() 
    over (
        PARTITION BY [group_by_field] 
        order by [prioritise_field]
    ) AS RowNo 
    FROM [table_name]
)
SELECT * FROM TOPTEN WHERE RowNo <= 10
80
Phil Rabbitt

Cela fonctionne sur SQL Server 2005 (modifié pour refléter votre clarification):

select *
from Things t
where t.ThingID in (
    select top 10 ThingID
    from Things tt
    where tt.Section = t.Section and tt.ThingDate = @Date
    order by tt.DateEntered desc
    )
    and t.ThingDate = @Date
order by Section, DateEntered desc
35
Matt Hamilton
SELECT r.*
FROM
(
    SELECT
        r.*,
        ROW_NUMBER() OVER(PARTITION BY r.[SectionID] ORDER BY r.[DateEntered] DESC) rn
    FROM [Records] r
) r
WHERE r.rn <= 10
ORDER BY r.[DateEntered] DESC
27
lorond

Je le fais de cette façon:

SELECT a.* FROM articles AS a
  LEFT JOIN articles AS a2 
    ON a.section = a2.section AND a.article_date <= a2.article_date
GROUP BY a.article_id
HAVING COUNT(*) <= 10;

pdate: Cet exemple de GROUP BY fonctionne uniquement avec MySQL et SQLite, car ces bases de données sont plus permissives que le SQL standard concernant GROUP BY. La plupart des implémentations SQL exigent que toutes les colonnes de la liste de sélection qui ne font pas partie d'une expression d'agrégat figurent également dans GROUP BY.

18
Bill Karwin

Si vous connaissez les sections, vous pouvez faire:

select top 10 * from table where section=1
union
select top 10 * from table where section=2
union
select top 10 * from table where section=3
9
Blorgbeard

Si nous utilisons SQL Server> = 2005, nous pouvons résoudre la tâche avec un seul sélectionner seulement:

declare @t table (
    Id      int ,
    Section int,
    Moment  date
);

insert into @t values
(   1   ,   1   , '2014-01-01'),
(   2   ,   1   , '2014-01-02'),
(   3   ,   1   , '2014-01-03'),
(   4   ,   1   , '2014-01-04'),
(   5   ,   1   , '2014-01-05'),

(   6   ,   2   , '2014-02-06'),
(   7   ,   2   , '2014-02-07'),
(   8   ,   2   , '2014-02-08'),
(   9   ,   2   , '2014-02-09'),
(   10  ,   2   , '2014-02-10'),

(   11  ,   3   , '2014-03-11'),
(   12  ,   3   , '2014-03-12'),
(   13  ,   3   , '2014-03-13'),
(   14  ,   3   , '2014-03-14'),
(   15  ,   3   , '2014-03-15');


-- TWO earliest records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case when row_number() over(partition by Section order by Moment) <= 2 then 0 else 1 end;


-- THREE earliest records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case when row_number() over(partition by Section order by Moment) <= 3 then 0 else 1 end;


-- three LATEST records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case when row_number() over(partition by Section order by Moment desc) <= 3 then 0 else 1 end;
9
Vadim Loboda

Je sais que ce fil est un peu vieux mais je viens de tomber sur un problème similaire (sélectionnez le dernier article de chaque catégorie) et voici la solution que j'ai trouvée:

WITH [TopCategoryArticles] AS (
    SELECT 
        [ArticleID],
        ROW_NUMBER() OVER (
            PARTITION BY [ArticleCategoryID]
            ORDER BY [ArticleDate] DESC
        ) AS [Order]
    FROM [dbo].[Articles]
)
SELECT [Articles].* 
FROM 
    [TopCategoryArticles] LEFT JOIN 
    [dbo].[Articles] ON
        [TopCategoryArticles].[ArticleID] = [Articles].[ArticleID]
WHERE [TopCategoryArticles].[Order] = 1

Ceci est très similaire à la solution de Darrel mais résout le problème RANK qui peut retourner plus de lignes que prévu.

8
Diadistis

Essayé ce qui suit et cela a fonctionné avec des liens aussi.

SELECT rs.Field1,rs.Field2 
FROM (
    SELECT Field1,Field2, ROW_NUMBER() 
      OVER (Partition BY Section
            ORDER BY RankCriteria DESC ) AS Rank
    FROM table
    ) rs WHERE Rank <= 10
6
Raghu S

L’opérateur UNION pourrait-il fonctionner pour vous? Avoir un SELECT pour chaque section, puis UNION les ensemble. Je suppose que cela ne fonctionnerait que pour un nombre déterminé de sections.

4
sblundy

Q) Recherche des enregistrements TOP X de chaque groupe (Oracle)

SQL> select * from emp e 
  2  where e.empno in (select d.empno from emp d 
  3  where d.deptno=e.deptno and rownum<3)
  4  order by deptno
  5  ;

 EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO

  7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10
  7839 KING       PRESIDENT            17-NOV-81       5000                    10
  7369 SMITH      CLERK           7902 17-DEC-80        800                    20
  7566 JONES      MANAGER         7839 02-APR-81       2975                    20
  7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
  7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30

6 lignes sélectionnées.


4
bharathreddy

Si vous souhaitez produire une sortie groupée par section, affichez uniquement les enregistrements n supérieurs de chaque section, à peu près comme ceci:

SECTION     SUBSECTION

deer        American Elk/Wapiti
deer        Chinese Water Deer
dog         Cocker Spaniel
dog         German Shephard
horse       Appaloosa
horse       Morgan

... alors ce qui suit devrait fonctionner de manière générique avec toutes les bases de données SQL. Si vous voulez que le top 10, changez simplement le 2 en 10 vers la fin de la requête.

select
    x1.section
    , x1.subsection
from example x1
where
    (
    select count(*)
    from example x2
    where x2.section = x1.section
    and x2.subsection <= x1.subsection
    ) <= 2
order by section, subsection;

Installer:

create table example ( id int, section varchar(25), subsection varchar(25) );

insert into example select 0, 'dog', 'Labrador Retriever';
insert into example select 1, 'deer', 'Whitetail';
insert into example select 2, 'horse', 'Morgan';
insert into example select 3, 'horse', 'Tarpan';
insert into example select 4, 'deer', 'Row';
insert into example select 5, 'horse', 'Appaloosa';
insert into example select 6, 'dog', 'German Shephard';
insert into example select 7, 'horse', 'Thoroughbred';
insert into example select 8, 'dog', 'Mutt';
insert into example select 9, 'horse', 'Welara Pony';
insert into example select 10, 'dog', 'Cocker Spaniel';
insert into example select 11, 'deer', 'American Elk/Wapiti';
insert into example select 12, 'horse', 'Shetland Pony';
insert into example select 13, 'deer', 'Chinese Water Deer';
insert into example select 14, 'deer', 'Fallow';
3
Craig

Alors que la question concernait SQL Server 2005, la plupart des gens sont passés à autre chose et s’ils le trouvent, quelle pourrait être la réponse préférée dans d’autres situations? utilisant CROSS APPLY comme illustré dans cet article de blog .

SELECT *
FROM t
CROSS APPLY (
  SELECT TOP 10 u.*
  FROM u
  WHERE u.t_id = t.t_id
  ORDER BY u.something DESC
) u

Cette requête implique 2 tables. La requête de l'OP implique une seule table, dans le cas ou une solution basée sur une fonction de fenêtre pourrait être plus efficace.

2
Lukas Eder

Vous pouvez essayer cette approche. Cette requête renvoie 10 villes les plus peuplées de chaque pays.

   SELECT city, country, population
   FROM
   (SELECT city, country, population, 
   @country_rank := IF(@current_country = country, @country_rank + 1, 1) AS country_rank,
   @current_country := country 
   FROM cities
   ORDER BY country, population DESC
   ) ranked
   WHERE country_rank <= 10;
1
Ali