Выберите 10 лучших записей для каждой категории

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

База данных - это SQL Server 2005. Я хочу вернуть первую десятку по дате. Разделы являются деловыми, локальными и функциональными. За одну конкретную дату я хочу только верхние (10) бизнес-строки (самая последняя запись), верхние (10) локальные строки и верхние (10) функции.

Ответ 1

Если вы используете 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 строк, и решение Matt может быть лучше для вас.

Ответ 2

В 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

Ответ 3

Это работает на 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

Ответ 4

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

Ответ 5

Я делаю так:

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.

Ответ 6

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

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

Ответ 7

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

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, которая может вернуть больше строк, чем предполагалось.

Ответ 8

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

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

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

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

Ответ 10

Может ли работать оператор UNION? У вас есть один SELECT для каждой секции, затем СОЕДИНЯЙТЕ их вместе. Полагаю, что это будет работать только для фиксированного количества разделов.

Ответ 11

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 строк.


Ответ 12

Если вы хотите создать вывод, сгруппированный по разделу, отображающий только верхние n записей из каждого раздела примерно так:

SECTION     SUBSECTION

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

... тогда следующее должно работать в целом в целом со всеми базами данных SQL. Если вы хотите 10 лучших, просто измените 2 на 10 в конец запроса.

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;

Чтобы настроить:

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';

Ответ 13

Вы можете попробовать этот подход. Этот запрос возвращает 10 наиболее населенных городов для каждой страны.

   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;

Ответ 14

Хотя вопрос был о SQL Server 2005, большинство людей пошли дальше, и если они найдут этот вопрос, то в других ситуациях предпочтительным ответом может быть тот, который использует CROSS APPLY как показано в этом сообщении в блоге.

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

Этот запрос включает в себя 2 таблицы. Запрос OP включает только 1 таблицу, в случае которой решение на основе оконной функции может быть более эффективным.

Ответ 15

Для разработчика WordPress вы можете использовать этот sql для выбора топ-5 сообщений из определенных категорий:

SELECT
    t.term_id, 
    p.ID,
    p.post_title, 
    @counter := IF(@current_term = t.name , @counter := @counter+1, @counter := 0) AS counter,
    @current_term := t.name as name

FROM tb_posts p

LEFT JOIN tb_term_relationships rel ON rel.object_id = p.ID
LEFT JOIN tb_term_taxonomy tax ON tax.term_taxonomy_id = rel.term_taxonomy_id 
LEFT JOIN tb_terms t ON t.term_id = tax.term_id

WHERE 
    post_type='post' AND post_status='publish' AND tax.taxonomy = 'category' 
AND 
    t.term_id IN (1,2,3,4) -- specific categories
having 
    counter < 5
ORDER BY 
    t.term_id;