2008-10-07 19 views
142

Quiero devolver los 10 registros principales de cada sección en una consulta. ¿Alguien puede ayudar con cómo hacerlo? La sección es una de las columnas en la tabla.Seleccione los 10 registros principales para cada categoría

La base de datos es SQL Server 2005. Quiero devolver los 10 primeros por fecha ingresada. Las secciones son de negocios, locales y funciones. Para una fecha en particular, solo deseo las primeras (10) filas de negocios (entrada más reciente), las mejores (10) filas locales y las principales (10).

+0

¿Alguna de estas respuestas funcionó para usted? –

Respuesta

4

¿Puede el operador UNION trabajar para usted? Tenga un SELECT para cada sección, luego UNIONALOS juntos. Supongo que solo funcionaría para un número fijo de secciones.

28

Esto funciona en SQL Server 2005 (corregido para reflejar su aclaración):

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

Esto no funciona para las filas donde la Sección es nula, sin embargo. Debería decir "dónde (tt.Section es nulo y t.Section es nulo) o tt.Section = t.Section" –

9

Si sabe lo que las secciones son, que puede hacer:

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

Esta sería la forma más fácil de hacerlo. –

+1

Pero esto sería ineficiente si tiene 150 o si las categorías son variables por día, semana, etc. –

+0

Claro, pero para citar OP: "Las secciones son de negocios, locales y función". Si tiene tres categorías estáticas, esta es la mejor manera de hacerlo. – Blorgbeard

166

Si está utilizando SQL 2005 puedes hacer algo como esto ...

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 tu RankCriteria tiene vínculos, entonces puedes devolver más de 1 0 filas y la solución de Matt puede ser mejor para usted.

+25

Si realmente solo quiere los 10 primeros, cámbielo a RowNumber() en lugar de Rank(). No hay lazos entonces. –

+3

Esto funciona, pero tenga en cuenta que el planificador de consultas puede convertir la clasificación() en una clasificación de tabla completa si no hay un índice cuya clave * first * es RankCriteria. En este caso, puede obtener un mejor millaje seleccionando las distintas secciones y la aplicación cruzada para elegir los 10 mejores ordenados por RankCriteria desc. –

+0

¡Gran respuesta! Me consiguió casi exactamente lo que necesitaba. Terminé yendo con 'DENSE_RANK' que no tiene lagunas en la numeración. +1 –

15

lo hago de esta manera:

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; 

actualización: Este ejemplo de GROUP BY obras en MySQL y SQLite única, porque esas bases de datos son más permisivos que SQL estándar con respecto GROUP BY. La mayoría de las implementaciones SQL requieren que todas las columnas en la lista de selección que no son parte de una expresión agregada también estén en GROUP BY.

+1

¿Eso funciona? Estoy bastante seguro de que "a.somecolumn no es válido en la lista de selección ya que no está contenido en una función agregada o en la cláusula group by" para cada columna en los artículos, excepto article_id .. – Blorgbeard

+1

Debería poder incluir otras columnas que dependen funcionalmente de la (s) columna (s) nombradas en GROUP BY. Las columnas que no son funcionalmente dependientes son ambiguas. Pero tiene razón, dependiendo de la implementación de RDBMS. Funciona en MySQL pero IIRC falla en InterBase/Firebird. –

+1

¿Funcionaría esto en el caso de que los once registros principales de una sección tuvieran la misma fecha? Todos tendrían recuentos de 11 y el resultado sería un conjunto vacío. – Arth

8

Sé que este hilo es un poco viejo pero yo sólo he tropezado con un problema similar (seleccione el artículo más reciente de cada categoría) y esta es la solución que se me ocurrió:

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 

Este es muy similar a la solución de Darrel, pero supera el problema de RANK que podría devolver más filas de lo previsto.

+0

¿Por qué usar CTE Sir? ¿Se reduce el consumo de memoria? – toha

4

Q) registros Finding 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 filas seleccionadas.


+0

La pregunta era sobre SQL Server, no Oracle. – Craig

18
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 
+0

¿Qué es la tabla con alias 'm'? – Chalky

+0

@Chalky es un error tipográfico, debería ser 'r'. fijo. – lorond

+0

Funcionó como un encanto. ¡Gracias! –

57

En T-SQL, lo haría:

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

: Sea más descriptivo sobre su solución. Consulte: [Cómo responder] (http://stackoverflow.com/questions/how-to-answer) – askmish

+0

¿La consulta de selección en CTE puede contener la cláusula where? – toha

+1

@toha Sí, puede – KindaTechy

3

Si desea producir una salida agrupados por sección, mostrando sólo los mejores n registros de cada sección algo como esto :

SECTION  SUBSECTION 

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

...entonces, lo siguiente debería funcionar bastante genéricamente con todas las bases de datos SQL. Si quiere los 10 mejores, simplemente cambie los 2 a un 10 hacia el final de la 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'; 
+0

Esto no funciona cuando solo quiero el primer registro para cada sección. Elimina todos los grupos de secciones que tienen más de 1 registro. Intenté reemplazando <= 2 con <= 1 – nils

+0

@nils Solo hay tres valores de sección: ciervo, perro y caballo. Si cambia la consulta a <= 1, obtendrá una subsección para cada sección: American Elk/Wapiti para ciervos, Cocker Spaniel para perros y Appaloosa para caballos. Estos también son los primeros valores en cada sección alfabéticamente. La consulta * significa * para eliminar todos los otros valores. – Craig

+0

Pero cuando intento ejecutar su consulta, elimina todo porque el recuento es> = 1 para todo. No conserva la primera subsección para cada sección. ¿Puedes intentar ejecutar tu consulta para <= 1 y decirme si obtienes la primera subsección para cada sección? – nils

5

Si usamos SQL Server> = 2005, entonces podemos resolver la tarea con una seleccione solamente:

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; 
+0

+1 Me gusta esta solución por su simplicidad, pero ¿podría explicar cómo funciona 'top 1' con la sentencia' case' en la cláusula 'order by' que devuelve 0 o 1? – Ceres

+2

TOP 1 funciona con WITH TIES aquí. WITH TIES significa que cuando ORDER BY = 0, entonces SELECT toma este registro (debido a TOP 1) y todos los demás que tienen ORDER BY = 0 (debido a WITH TIES) –

0

Usted puede tratar Este enfoque. Esta consulta devuelve 10 ciudades más pobladas de 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; 
2

Intenté lo siguiente y funcionó con los lazos también.

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