Дополнительные поля с SQL MIN() и ГРУППИРОВАТЬ ПО
-
05-09-2019 - |
Вопрос
При использовании функции SQL MIN() вместе с GROUP BY будут ли какие-либо дополнительные столбцы (не столбец MIN или один из столбцов GROUP BY) соответствовать данным в соответствующей строке MIN?
Например, дана таблица с названиями отделов, именами сотрудников и зарплатой:
SELECT MIN(e.salary), e.* FROM employee e GROUP BY department
Очевидно, что у меня будут две хорошие колонки: о минимальной зарплате и отделе.Будет ли имя сотрудника (и любые другие поля сотрудника) из той же строки?А именно строка с MIN (зарплата)?
Я знаю, что вполне возможно, что есть два сотрудника с одинаковой (и самой низкой) зарплатой, но все, что меня интересует (сейчас), - это получение всей информации о (или один - единственный) самый дешевый сотрудник.
Выберет ли это самого дешевого продавца?
SELECT min(salary), e.* FROM employee e WHERE department = 'sales'
По сути, могу ли я быть уверен, что данные, возвращаемые вместе с функцией MIN(), будут соответствовать (или один - единственный) запись с этим минимальным значением?
Если база данных имеет значение, я работаю с MySQL.
Решение
Если бы вы хотели заполучить самого "дешевого" сотрудника в каждом отделе, у вас было бы два варианта, которые мне не приходили в голову:
SELECT
E.* -- Don't actually use *, list out all of your columns
FROM
Employees E
INNER JOIN
(
SELECT
department,
MIN(salary) AS min_salary
FROM
Employees
GROUP BY
department
) AS SQ ON
SQ.department = E.department AND
SQ.min_salary = E.salary
Или вы можете использовать:
SELECT
E.*
FROM
Employees E1
LEFT OUTER JOIN Employees E2 ON
E2.department = E1.department AND
E2.salary < E1.salary
WHERE
E2.employee_id IS NULL -- You can use any NOT NULL column here
Второе утверждение эффективно работает, говоря: покажите мне всех сотрудников, где вы не можете найти другого сотрудника в том же отделе с более низкой зарплатой.
В обоих случаях, если два или более сотрудника имеют одинаковую минимальную заработную плату, вы получите их обоих (всех).
Другие советы
SELECT e.*
FROM employee e
WHERE e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = 'sales'
ORDER BY
e.salary
LIMIT 1
)
Чтобы получить значения для каждого отдела, используйте:
SELECT e.*
FROM department d
LEFT JOIN
employee e
ON e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = d.id
ORDER BY
e.salary
LIMIT 1
)
Чтобы получить значения только для тех отделов, в которых есть сотрудники, используйте:
SELECT e.*
FROM (
SELECT DISTINCT eo.department
FROM employee eo
) d
JOIN
employee e
ON e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = d.department
ORDER BY
e.salary
LIMIT 1
)
Конечно, имея индекс на (department, salary)
значительно улучшит все три запроса.
Самое быстрое решение:
SET @dep := '';
SELECT * FROM (
SELECT * FROM `employee` ORDER BY `department`, `salary`
) AS t WHERE IF ( @dep = t.`department`, FALSE, ( @dep := t.`department` ) OR TRUE );
Другим подходом может быть использование аналитических функций.Вот запрос, использующий аналитические функции и ROW_NUM
выберите first_name, зарплата из (выберите first_name, зарплата, Row_NUMBER() поверх (РАЗДЕЛЕНИЕ ПО DEPARTMENT_ID ЗАКАЗ По окладу ASC) в качестве row_count из employees) где row_count=1;