2010-12-14 12 views
6

tengo una consulta como esta:consulta para encontrar el primer y el segundo valor más grande de un grupo

SELECT 
DATEPART(year,some_date), 
DATEPART(month,some_date), 
MAX(some_value) max_value 
FROM 
some_table 
GROUP BY 
    DATEPART(year,some_date), 
    DATEPART(month,some_date) 

Esto devuelve una tabla con: año, el mes, el valor más alto para el mes.

me gustaría modificar la consulta de modo que pudiera obtener: año, el mes, el valor más alto para el mes, el segundo valor más grande para el mes en cada fila.

Me parece que las soluciones conocidas como "TOP 2", "NOT IN TOP 1" o una subselección no funcionarán aquí.

(a ser muy específico - estoy usando SQL Server 2008.)

Cualquier ayuda apreciada, THX.

Respuesta

3

Me parece que la cuestión exige una consulta que devolvería mejor y el segundo mejor en la misma fila para cada mes y año, así:

month, year, best, second best 
... 
... 

y no dos filas para el mismo mes y año que contiene el mejor y el segundo mejor valor.

Esta es la solución que se me ocurrió, por lo que si alguien tiene una forma más sencilla de lograr esto, me gustaría saber.

with ranks as (
    select 
     year(entrydate) as [year], 
     month(entrydate) as [month], 
     views, 
     rank() over (partition by year(entrydate), month(entrydate) order by views desc) as [rank] 
    from product 
) 
select 
    t1.year, 
    t1.month, 
    t1.views as [best], 
    t2.views as [second best] 
from ranks t1 
    inner join ranks t2 
     on t1.year = t2.year 
     and t1.month = t2.month 
     and t1.rank = 1 
     and t2.rank = 2 

EDIT: Sólo por curiosidad lo hice un poco más pruebas y terminó con una variación más simple en el Stephanie Page's answer que no utiliza una subconsulta adicional. Y cambié la función rank() a row_number() ya que no funciona cuando dos valores máximos son iguales.

with ranks as (
    select 
     year(entrydate) as [year], 
     month(entrydate) as [month], 
     views, 
     row_number() over (partition by year(entrydate), month(entrydate) order by views desc) as [rank] 
    from product 
) 
select 
    t1.year, 
    t1.month, 
    max(case when t1.rank = 1 then t1.views else 0 end) as [best], 
    max(case when t1.rank = 2 then t1.views else 0 end) as [second best] 
from 
    ranks t1 
where 
    t1.rank in (1,2) 
group by 
    t1.year, t1.month 
+0

Sí, esta es definitivamente una solución de trabajo, gracias. Creo que hay varias soluciones similares que usan combinaciones, pero todavía me pregunto (o mejor dicho, simplemente tengo curiosidad) si hay alguna sin ellas. – Helena

+0

Me encontré con problemas similares a lo largo de los años y también me preguntaba si existe una solución más simple (sin combinaciones tal vez), pero no pude corregirla. Y a medida que resulta la solución de unión, aunque parezca un error, funcione perfectamente y lo suficientemente rápido. – skajfes

+0

@Helena: por cierto vote y/o acepte la respuesta si cree que es útil, y bienvenido a stackoverflow – skajfes

1

Esto es un poco old-school pero TOP y una subconsulta funcionarán si usas ORDER BY. Prueba esto:

SELECT TOP 2 
DATEPART(year,some_date), 
DATEPART(month,some_date), 
(SELECT MAX(st1.some_value) FROM some_table AS st1 
    WHERE DATEPART(month,some_date) = DATEPART(month,st1.some_date)) AS max_value 
FROM 
some_table 
GROUP BY 
    DATEPART(year,some_date), 
    DATEPART(month,some_date) 
ORDER BY DATEPART(month,some_date) DESC 

que le dará las dos filas con los "más altos" valores del mes y la subselección añadido debería darle el máximo de cada agrupación.

+0

que quiere que el valor máximo y el valor máximo penúltima. –

+0

@Stephanie: Por favor, mira mi actualización. Me llevó algunas lecturas a través del OP para entender esa parte. –

+0

Afortunadamente pude cambiarlo –

0

Hmmm que es una especie de plataforma, pero se puede hacer esto con subconsultas ... en vez de usar que Max me había seleccione los some_values ​​que tengan el año coincidente & month, row_number() = 1/row_number() = 2 respectivamente y ordene por some_value DESC.

La incapacidad de utilizar OFFSET/LIMIT como se puede en SQLite es una de mis aversiones acerca de SQL Server.

+0

SQL Server 2011 ** tendrá ** OFFSET y LIMIT - vea http://msdn.microsoft.com/en-us/library/ ms188385% 28v = sql.110% 29.aspx –

+0

@marc_s: finalmente, unos diez años demasiado tarde si me preguntas – skajfes

1

Puede utilizar un CTE con las funciones de clasificación en SQL Server 2005 y hasta:

;WITH TopValues AS 
(
    SELECT 
    YEAR(some_date) AS 'Year', 
    MONTH(some_date) AS 'Month', 
    Some_Value, 
    ROW_NUMBER() OVER(PARTITION BY YEAR(some_date),MONTH(some_date) 
         ORDER BY Some_Value DESC) AS 'RowNumber' 
    FROM 
    dbo.some_table 
) 
SELECT 
    Year, Month, Some_Value 
FROM 
    TopValues 
WHERE 
    RowNumber <= 2 

Esto "partición" (es decir, grupo) los datos por mes/año, el orden dentro de cada grupo por Some_Value descendente (más grande primero), y luego puede seleccionar los primeros dos de cada grupo de ese CTE.

RANK() funciona también (casi siempre uso ROW_NUMBER) - aunque produce resultados ligeramente diferentes - realmente depende de cuáles sean sus necesidades.

+1

te da los dos valores, pero no en la misma fila. –

+0

@Stephanie Página: No vi esto como un requisito, pero sí, eso es cierto, enumeraría los dos valores principales por separado, cada uno en una fila de datos separada –

2

para hacer esto sin juntas (Voy a mostrar el oráculo ... usted acaba de casos de uso en lugar de decodifica)

with ranks as (
     select 
      year(entrydate) as [year], 
      month(entrydate) as [month], 
      views, 
      rank() over (partition by year(entrydate), month(entrydate) order by views desc) as [rank] 
     from product 
    ) 
SELECT [year], [month], Max([best]), Max([second best]) 
FROM 
    (select 
     t1.year, 
     t1.month, 
     Decode([rank],1,t1.views,0) as [best], 
     Decode([rank],2,t1.views,0) as [second best] 
    from ranks t1 
    where t1.rank <= 2) x 
GROUP BY [year], [month] 
+0

Bien, me gusta el uso de decodificación/función de caja. – skajfes

Cuestiones relacionadas