2009-07-31 26 views
6

Tengo una consulta en la línea deHacer una consulta SQL más eficiente

select b.* from 
(select key, max(val) as val from (somequery) group by key) as a 
inner join 
(somequery) as b 
on a.key = b.key and a.val = b.val 
order by key 

Y me preguntaba si hay una manera obvia (que me falta) para simplificarlo (dado que somequery podría ser bastante largo).

Cualquier pensamiento sería apreciado.

+0

¿alguna pregunta? Tendrá que profundizar en eso si espera obtener alguna ayuda real. –

+1

@rexem: No, el OP no lo hace. Es obvio que solo quiere replegar filas con el valor máximo. – Eric

Respuesta

2

hay, pero ciertamente no es obvia:

select 
    * 
from 
    (
    select 
     key, 
     val, 
     col, 
     max(val) over (partition by key) as MaxVal 
    from 
     tableA 
    ) 
where 
    val = MaxVal 

El uso de la cláusula de over es una gran manera de hacer esto, y no requiere ninguna subconsulta extraños. Todo lo que hace es tomar el máximo de val por cada clave, y luego ajusta ese conjunto de resultados en una subconsulta, donde podemos verificar el val contra MaxVal para asegurarnos de que estamos tirando de la fila correcta.

¡Mucho más limpio y más rápido que hacer hasta tres subconsultas!

+0

No he visto MAX..OVER antes. ¿Cómo funciona esto para las claves compuestas? Estoy probando mi propia mesa y puedo usar el agg/join o ROWNUMBER = 1, pero no esta técnica. – gbn

+0

'max (val) over (partición por clave1, clave2, clave3)' funciona como un amuleto. – Eric

+0

Gracias por eso, Eric. –

-1

Seleccionaría sus subconsultas en tablas temporales antes de seleccionarlas. Creo que verías un impulso significativo en el rendimiento por eso.

+1

No es mi voto negativo, pero ... si selecciona en una tabla temporal, el DBMS tiene que almacenar esa información, y sus metadatos, y luego usarla una vez. Sería más eficaz y efectivo dar al optimizador toda la consulta, por lo que puede evitar materializar la subconsulta si es posible. Ahora, si hay varias consultas que utilizarán la misma subconsulta, entonces es posible que vea un beneficio de una tabla temporal explícita, aunque sería una buena idea medir y asegurarse. –

+1

@Jonathan: Estoy de acuerdo en que, en principio, lo que dices es correcto, pero mi experiencia con SQL Server sugiere que para tablas más grandes (digamos algo más de 2000 filas) esta ruta es mucho más eficiente. – Jon

0

Desea usar ROW_NUMBER() o RANK() para esto.

(y por favor asegúrese de que una consulta anterior termina con un punto y coma)

with ranked as 
(
select *, row_number() over (partition by key order by val desc) as bestrow 
from sometableorquery 
) 
select * 
from ranked 
where bestrow = 1 
order by key; 

Si desea lazos (de modo que una llave que tiene dos mejores valores devuelve ambos), a continuación, utilizar rango() en lugar de row_number().

Rob

+0

Además, si desea obtener los 3 primeros de cada clave, intente con "DONDE bestrow <= 3" –

+1

ROW_NUMBER solo devolverá una fila por clave. Donde como el código del OP empataría en el caso de que haya más de una fila para una clave con val = max (val). Reemplazar ROW_NUMBER() con RANK() preservaría la intención original. –

+0

Gracias Shannon. Mencioné lo de "si quieres lazos" allí. –

Cuestiones relacionadas