2009-10-07 9 views
6

Soy nuevo en el trabajo con funciones analíticas.Oracle Analytic function for min value in grouping

 
DEPT EMP SALARY 
---- ----- ------ 
    10 MARY 100000 
    10 JOHN 200000 
    10 SCOTT 300000 
    20 BOB 100000 
    20 BETTY 200000 
    30 ALAN 100000 
    30 TOM 200000 
    30 JEFF 300000 

Quiero el departamento y el empleado con salario mínimo.

Los resultados deben verse como:

 
DEPT EMP SALARY 
---- ----- ------ 
    10 MARY 100000 
    20 BOB 100000 
    30 ALAN 100000 

EDIT: Aquí está el SQL He (pero por supuesto, no funciona como quiera personal en el grupo por la cláusula también):

 
SELECT dept, 
    emp, 
    MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) 
FROM mytable 
GROUP BY dept 

Respuesta

7

Creo que la función Rank() no es el camino a seguir con esto, por dos razones.

En primer lugar, es probablemente menos eficiente que un método basado en Min().

La razón de esto es que la consulta debe mantener una lista ordenada de todos los salarios por departamento a medida que escanea los datos, y luego se asignará el rango más adelante al volver a leer esta lista. Obviamente, en ausencia de índices que puedan aprovecharse para esto, no puede asignar un rango hasta que se haya leído el último elemento de datos, y el mantenimiento de la lista es costoso.

Por lo tanto, el rendimiento de la función Rank() depende del número total de elementos que se analizarán, y si el número es suficiente para que el ordenamiento se derrame en el disco, el rendimiento colapsará.

Esto es probablemente más eficiente:

select dept, 
     emp, 
     salary 
from 
     (
     SELECT dept, 
       emp, 
       salary, 
       Min(salary) Over (Partition By dept) min_salary 
     FROM mytable 
     ) 
where salary = min_salary 
/

Este método sólo requiere que la consulta a mantener un solo valor por cada departamento del valor mínimo encontrado hasta ahora. Si se encuentra un nuevo mínimo, se modifica el valor existente, de lo contrario, se descarta el nuevo valor. La cantidad total de elementos que deben mantenerse en la memoria se relaciona con la cantidad de departamentos, no con el número de filas escaneadas.

Podría ser que Oracle tenga una ruta de código para reconocer que en este caso realmente no es necesario calcular el rango, pero yo no apostaría.

La segunda razón para desagradar el rango() es que simplemente responde la pregunta incorrecta. La pregunta no es "¿Qué registros tienen el salario que es la primera clasificación cuando los salarios por departamento están ordenados ascendentemente?", Es "Qué registros tienen el salario que es el mínimo por departamento". Eso hace una gran diferencia para mí, al menos.

+0

Gracias David. Después de considerar sus beneficios, reformulé su solución. –

3

Puede usar la sintaxis RANK(). Por ejemplo, esta consulta le dirá que un empleado se ubica dentro de su departamento con respecto a lo grande que su salario es:

SELECT 
    dept, 
    emp, 
    salary, 
    (RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept 
FROM EMPLOYEES 

A continuación, puede consultar desde aquí donde salary_rank_within_dept = 1:

SELECT * FROM 
    (
    SELECT 
     dept, 
     emp, 
     salary, 
     (RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept 
    FROM EMPLOYEES 
) 
WHERE salary_rank_within_dept = 1 
+0

¡Perfecto! Aún no sabía sobre RANK(). Gracias. –

+0

¡Ni siquiera sabía acerca de RANK() hasta ayer! :) –

+1

Estoy bajando la votación por las razones que describí en mi propia respuesta: creo que probablemente sea ineficiente, y creo que la consulta no concuerda bien con la pregunta exacta que se hace. No estoy diciendo que no dará la respuesta correcta, solo que no expresa muy bien la lógica de la pregunta. –

-1
select e2.dept, e2.emp, e2.salary 
from employee e2 
where e2.salary = (select min(e1.salary) from employee e1) 
+1

Eso le dará un registro, el mínimo para toda la tabla. Necesita agrupar por departamento en su subselección. –

3

Creo que estuvo muy cerca de su consulta original. El siguiente sería correr y hacer coincidir el caso de test:

SELECT dept, 
    MIN(emp) KEEP(DENSE_RANK FIRST ORDER BY salary, ROWID) AS emp, 
    MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary, ROWID) AS salary 
FROM mytable 
GROUP BY dept 

En contraste con el RANK (soluciones), éste garantiza a lo sumo una fila por cada departamento.Pero eso sugiere un problema: ¿qué sucede en un departamento donde hay dos empleados con el salario más bajo? Las soluciones RANK() devolverán ambos empleados: más de una fila para el departamento. Esta respuesta elegirá una arbitrariamente y se asegurará de que solo haya una para el departamento.

+1

Sí, ese es un buen punto en los registros múltiples. El (los) método (s) Min (s) recuperarán todos los duplicados ... será más complicado recuperar una sola grabación para aquellos si fuera necesario. –

+1

Excelente elaboración, especialmente si el análisis que se plantea está más relacionado con el * valor * del mínimo. Si * se necesitan atributos * identificativos del mínimo, la conservación de los duplicados parece ser deseable. – Andrew

Cuestiones relacionadas