Аналитическая функция Oracle для определения минимального значения при группировании
-
20-09-2019 - |
Вопрос
Я новичок в работе с аналитическими функциями.
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
Мне нужен отдел и сотрудник с минимальной зарплатой.
Результаты должны выглядеть следующим образом:
DEPT EMP SALARY ---- ----- ------ 10 MARY 100000 20 BOB 100000 30 ALAN 100000
Редактировать:Вот SQL, который у меня есть (но, конечно, он не работает, так как ему также нужен staff в предложении group by):
SELECT dept, emp, MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) FROM mytable GROUP BY dept
Решение
Я думаю, что функция Rank() не подходит для этого по двум причинам.
Во-первых, это, вероятно, менее эффективно, чем метод, основанный на Min().
Причина этого заключается в том, что запрос должен поддерживать упорядоченный список всех зарплат по отделам при сканировании данных, а ранг будет присвоен позже при повторном чтении этого списка.Очевидно, что при отсутствии индексов, которые можно было бы использовать для этого, вы не можете присвоить ранг до тех пор, пока не будет прочитан последний элемент данных, а обслуживание списка обходится дорого.
Таким образом, производительность функции Rank() зависит от общего количества элементов, подлежащих проверке, и если этого количества достаточно, чтобы сортировка была перенесена на диск, то производительность упадет.
Это, вероятно, более эффективно:
select dept,
emp,
salary
from
(
SELECT dept,
emp,
salary,
Min(salary) Over (Partition By dept) min_salary
FROM mytable
)
where salary = min_salary
/
Этот метод требует, чтобы запрос поддерживал только одно значение для каждого отдела из минимального значения, встречавшегося до сих пор.Если встречается новый минимум, то существующее значение изменяется, в противном случае новое значение отбрасывается.Общее количество элементов, которые должны храниться в памяти, связано с количеством отделов, а не с количеством отсканированных строк.
Возможно, у Oracle есть кодовый путь для распознавания того, что в данном случае Ранг на самом деле не нужно вычислять, но я бы не стал делать ставку на это.
Вторая причина нелюбви к Rank() заключается в том, что она просто отвечает на неправильный вопрос.Вопрос не в том, "Какие записи имеют зарплату, которая занимает первое место в рейтинге, когда зарплаты для каждого отдела упорядочены по возрастанию", а в том, "Какие записи имеют минимальную зарплату для каждого отдела".По крайней мере, для меня это имеет большое значение.
Другие советы
Я думаю, вы были довольно близки к своему первоначальному запросу.Следующее будет выполнено и действительно соответствует вашему тестовому варианту:
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
В отличие от решений RANK(), это гарантирует не более одной строки на отдел.Но это намекает на проблему:что происходит в отделе, где есть два сотрудника с самой низкой зарплатой?Решения RANK() вернут обоих сотрудников - более одной строки для отдела.В этом ответе вы выберете один из них произвольно и убедитесь, что для отдела существует только один.
Вы можете использовать RANK()
синтаксис.Например, этот запрос сообщит вам, какое место занимает сотрудник в своем отделе в зависимости от того, насколько велика его зарплата:
SELECT
dept,
emp,
salary,
(RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept
FROM EMPLOYEES
Затем вы могли бы запросить из этого, где 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)