Função analítica do Oracle para valor mínimo no agrupamento
-
20-09-2019 - |
Pergunta
Sou novo em trabalhar com funções analíticas.
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
Quero o departamento e o funcionário com salário mínimo.
Os resultados devem parecer:
DEPT EMP SALARY ---- ----- ------ 10 MARY 100000 20 BOB 100000 30 ALAN 100000
EDIT: Aqui está o SQL que eu tenho (mas é claro, ele também não funciona, pois deseja a equipe do grupo por cláusula):
SELECT dept, emp, MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) FROM mytable GROUP BY dept
Solução
Eu acho que a função de classificação () não é o caminho a seguir com isso, por duas razões.
Em primeiro lugar, provavelmente é menos eficiente que um método baseado em min ().
A razão para isso é que a consulta deve manter uma lista ordenada de todos os salários por departamento, pois verifica os dados, e a classificação será atribuída posteriormente relendo esta lista. Obviamente, na ausência de índices que podem ser aproveitados para isso, você não pode atribuir uma classificação até que o último item de dados seja lido e a manutenção da lista seja cara.
Portanto, o desempenho da função de classificação () depende do número total de elementos a serem digitalizados e, se o número for suficiente para que a classificação derrame para o disco, o desempenho entrará em colapso.
Isso provavelmente é mais eficiente:
select dept,
emp,
salary
from
(
SELECT dept,
emp,
salary,
Min(salary) Over (Partition By dept) min_salary
FROM mytable
)
where salary = min_salary
/
Esse método requer apenas que a consulta mantenha um único valor por departamento do valor mínimo encontrado até agora. Se um novo mínimo for encontrado, o valor existente será modificado, caso contrário, o novo valor será descartado. O número total de elementos que precisam ser mantidos na memória está relacionado ao número de departamentos, não ao número de linhas digitalizadas.
Pode ser que o Oracle tenha um caminho de código para reconhecer que a classificação não precisa ser calculada neste caso, mas eu não apostaria nela.
A segunda razão para não gostar de classificação () é que ela apenas responde à pergunta errada. A questão não é "quais registros têm o salário que é o primeiro ranking quando os salários por departamento estão ascendentes ordenados", é "quais registros têm o salário que é o mínimo por departamento". Isso faz uma grande diferença para mim, pelo menos.
Outras dicas
Eu acho que você estava bem próximo da sua consulta original. O seguinte seria executado e corresponderia ao seu caso de teste:
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
Em contraste com as soluções de classificação (), este garante no máximo uma linha por departamento. Mas isso sugere um problema: o que acontece em um departamento onde há dois funcionários com o menor salário? As soluções de classificação () devolverão os dois funcionários - mais de uma linha para o departamento. Esta resposta escolherá um arbitrariamente e garantirá que haja apenas uma para o departamento.
Você pode usar o RANK()
sintaxe. Por exemplo, esta consulta dirá onde um funcionário está dentro do departamento em relação ao tamanho do seu salário:
SELECT
dept,
emp,
salary,
(RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept
FROM EMPLOYEES
Você poderia então consultar isso onde 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
select e2.dept, e2.emp, e2.salary
from employee e2
where e2.salary = (select min(e1.salary) from employee e1)