Campos extras com SQL MIN () e GROUP BY
-
05-09-2019 - |
Pergunta
Ao usar a função SQL MIN (), juntamente com GROUP BY, vai quaisquer colunas adicionais (não a coluna MIN, ou um do grupo por colunas) coincidir com os dados na correspondência MIN linha?
Por exemplo, dada uma tabela com nomes de departamento, nomes de funcionários e salário:
SELECT MIN(e.salary), e.* FROM employee e GROUP BY department
Obviamente Vou pegar duas boas colunas, do salário mínimo e do departamento. Será que o nome do funcionário (e quaisquer outros campos de funcionários) ser da mesma linha? Ou seja, a linha com o MIN (salário)?
Eu sei que há poderia muito possivelmente ser dois funcionários com o mesmo (e menor) salário, mas tudo que eu estou preocupado com a (agora) é obter todas as informações sobre o (ou um único ) empregado mais barato.
Será que essa escolha o vendedor mais barato?
SELECT min(salary), e.* FROM employee e WHERE department = 'sales'
Essencialmente, eu posso ter certeza de que os dados retornados juntamente com a função MIN () irá corresponder ao (ou a único) registro com que o valor mínimo?
Se as questões de banco de dados, eu estou trabalhando com o MySQL.
Solução
Se você quiser obter o "mais barato" empregado em cada departamento, você teria duas opções em cima da minha cabeça:
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
Ou você pode usar:
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
A segunda instrução funciona dizendo efetivamente, mostre-me todos os funcionários, onde você não pode encontrar um outro funcionário do mesmo departamento com um salário mais baixo.
Em ambos os casos, se dois ou mais funcionários têm salários iguais que são o mínimo que você vai buscá-los ambos (todos).
Outras dicas
SELECT e.*
FROM employee e
WHERE e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = 'sales'
ORDER BY
e.salary
LIMIT 1
)
Para obter valores para cada departamento, use:
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
)
Para obter valores apenas para os departamentos que têm empregados, de uso:
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
)
É claro que ter um índice em (department, salary)
vai melhorar muito as três consultas.
A solução mais rápida:
SET @dep := '';
SELECT * FROM (
SELECT * FROM `employee` ORDER BY `department`, `salary`
) AS t WHERE IF ( @dep = t.`department`, FALSE, ( @dep := t.`department` ) OR TRUE );
Outra abordagem pode ser o uso de funções analíticas. Aqui está a consulta usando funções analíticas e núm_linha
seleccione primeiro_nome, salário de (primeiro_nome seleccione, salário, row_number () ao longo de (partição POR ORDEM DEPARTMENT_ID pela ASC salário) como row_count dos empregados) onde row_count = 1;