Выберите 10 лучших записей для каждой категории - PullRequest
183 голосов
/ 07 октября 2008

Я хочу вернуть 10 лучших записей из каждого раздела в одном запросе. Может кто-нибудь помочь с тем, как это сделать? Раздел является одним из столбцов в таблице.

База данных - SQL Server 2005. Я хочу вернуть первые 10 по введенной дате. Разделы бизнес, локальные и тематические. Для одной конкретной даты мне нужны только верхние (10) бизнес-строк (самая последняя запись), верхние (10) локальные строки и верхние (10) объекты.

Ответы [ 14 ]

194 голосов
/ 07 октября 2008

Если вы используете SQL 2005, вы можете сделать что-то вроде этого ...

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

Если у вашего RankCriteria есть связи, вы можете вернуть более 10 строк, и решение Мэтта может быть лучше для вас.

77 голосов
/ 25 июня 2012

В T-SQL я бы сделал:

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
32 голосов
/ 07 октября 2008

Это работает на SQL Server 2005 (отредактировано для отражения ваших разъяснений):

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
24 голосов
/ 15 июня 2012
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
17 голосов
/ 07 октября 2008

Я делаю это так:

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;

update: Этот пример GROUP BY работает только в MySQL и SQLite, потому что эти базы данных более строгие, чем стандартный SQL в отношении GROUP BY. Большинство реализаций SQL требуют, чтобы все столбцы в списке выбора, которые не являются частью агрегатного выражения, также находились в GROUP BY.

9 голосов
/ 26 декабря 2014

Если мы используем SQL Server> = 2005, то мы можем решить задачу одним select only:

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 голосов
/ 07 октября 2008

Если вы знаете, что такое разделы, вы можете сделать:

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
8 голосов
/ 14 февраля 2011

Я знаю, что эта ветка немного устарела, но я только что столкнулся с подобной проблемой (выберите самую новую статью из каждой категории), и вот решение, которое я придумал:

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

Это очень похоже на решение Даррела, но преодолевает проблему RANK, которая может вернуть больше строк, чем предполагалось.

6 голосов
/ 12 ноября 2017

Попробовал следующее, и это работало и со связями.

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
4 голосов
/ 20 февраля 2011

Q) Поиск TOP X записей из каждой группы (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 выбранных строк.


...