Selecione os 10 principais registros para cada categoria

Desejo retornar os 10 principais registros de cada seção em uma consulta. Alguém pode ajudar com como fazer isso? Seção é uma das colunas na tabela.

Banco de dados é o SQL Server 2005. Eu quero retornar o top 10 por data inserida. As seções são comercial, local e recurso. Para uma data específica, quero apenas as 10 principais linhas de negócios (input mais recente), as 10 principais linhas locais e os 10 principais resources.

Se você estiver usando o SQL 2005, você pode fazer algo assim …

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 

Se o seu RankCriteria tiver laços, você pode retornar mais de 10 linhas e a solução de Matt pode ser melhor para você.

No T-SQL, eu faria:

 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 

Isso funciona no SQL Server 2005 (editado para refletir seu esclarecimento):

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

Eu faço assim:

 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: Este exemplo de GROUP BY funciona apenas no MySQL e no SQLite, porque esses bancos de dados são mais permissivos do que o SQL padrão referente ao GROUP BY. A maioria das implementações SQL exige que todas as colunas na lista de seleção que não fazem parte de uma expressão agregada também estejam no GROUP BY.

Se você souber quais são as seções, você pode fazer:

 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 

Eu sei que esta discussão é um pouco antiga, mas acabei de encontrar um problema semelhante (selecione o artigo mais recente de cada categoria) e esta é a solução que eu criei:

 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 

Isso é muito semelhante à solução de Darrel, mas supera o problema RANK que pode retornar mais linhas do que o pretendido.

Se usarmos o SQL Server> = 2005, poderemos resolver a tarefa com apenas uma seleção :

 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; 

O operador da UNION pode trabalhar para você? Tenha um SELECT para cada seção, depois UNION-os juntos. Acho que só funcionaria para um número fixo de seções.

P) Localizando os registros TOP X de cada grupo (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 linhas selecionadas.


Se você quiser produzir saída agrupada por seção, exibindo apenas os n registros superiores de cada seção, algo como isto:

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

… então o seguinte deve funcionar bem genericamente com todos os bancos de dados SQL. Se você quiser o top 10, apenas mude o 2 para o 10 no final da consulta.

 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; 

Para configurar:

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

Tentei o seguinte e funcionou com laços também.

 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 

Você pode tentar essa abordagem. Esta consulta retorna 10 cidades mais populosas para cada país.

  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; 

Embora a questão fosse sobre o SQL Server 2005, a maioria das pessoas mudou e, se encontrarem essa pergunta, o que poderia ser a resposta preferida em outras situações é uma usando CROSS APPLY conforme ilustrado neste post do 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 

Esta consulta envolve 2 tabelas. A consulta do OP envolve apenas 1 tabela, no caso em que uma solução baseada em function de janela pode ser mais eficiente.