I campi supplementari con SQL MIN () e GROUP BY
-
05-09-2019 - |
Domanda
Quando si utilizza la funzione SQL MIN (), insieme a GROUP BY, saranno eventuali ulteriori colonne (non la colonna MIN, o uno del gruppo per colonne) corrispondere ai dati nella riga corrispondente MIN?
Ad esempio, data una tabella con i nomi di reparto, i nomi dei dipendenti e lo stipendio:
SELECT MIN(e.salary), e.* FROM employee e GROUP BY department
Ovviamente vado a prendere due buoni colonne, il salario minimo e il reparto. Saranno il nome del dipendente (e qualsiasi altro campo dei dipendenti) sia dalla stessa riga? Ossia la riga con il MIN (stipendio)?
So che ci potrebbe essere molto probabilmente due dipendenti con lo stesso (e più bassa) lo stipendio, ma tutto quello che mi preoccupa (ora) sta ottenendo tutte le informazioni sul (o un singolo ) più economico dei dipendenti.
Sarebbe questo selezionare il venditore più economico?
SELECT min(salary), e.* FROM employee e WHERE department = 'sales'
In sostanza, posso essere sicuro che i dati restituiti con la funzione MIN () sarà partite il record (o un singolo ) con il valore minimo?
Se le questioni di database, sto lavorando con MySql.
Soluzione
Se si voleva ottenere il dipendente "più economico" in ogni reparto si avrebbe due scelte fuori della parte superiore della mia testa:
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
In alternativa si può usare:
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
La seconda affermazione funziona in modo efficace dicendo, mi mostra tutti i dipendenti in cui non si può trovare un altro dipendente nello stesso dipartimento, con un salario inferiore.
In entrambi i casi, se due o più dipendenti hanno stipendi uguali che sono il minimo si ottengono entrambi (tutti).
Altri suggerimenti
SELECT e.*
FROM employee e
WHERE e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = 'sales'
ORDER BY
e.salary
LIMIT 1
)
Per ottenere i valori per ogni reparto, uso:
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
)
Per ottenere i valori solo per quei reparti che hanno dipendenti, 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
)
Naturalmente, avere un indice su (department, salary)
migliorerà notevolmente tutte e tre le domande.
La soluzione più veloce:
SET @dep := '';
SELECT * FROM (
SELECT * FROM `employee` ORDER BY `department`, `salary`
) AS t WHERE IF ( @dep = t.`department`, FALSE, ( @dep := t.`department` ) OR TRUE );
Un altro approccio può essere utilizzando funzioni analitiche. Ecco la query utilizzando funzioni analitiche e row_num
SELECT nome, stipendio da (first_name di selezione, di stipendio, row_number () su (PARTITION BY DEPARTMENT_ID ORDER BY stipendio ASC) come row_count dai dipendenti) in cui row_count = 1;