Oracle analytische Funktion für Wert min in Gruppierungs
-
20-09-2019 - |
Frage
Ich bin neu mit analytischen Funktionen zu arbeiten.
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
Ich möchte die Abteilung und Mitarbeiter mit Mindestlohn.
Die Ergebnisse sollen wie folgt aussehen:
DEPT EMP SALARY ---- ----- ------ 10 MARY 100000 20 BOB 100000 30 ALAN 100000
EDIT: Hier ist die SQL Ich habe (aber natürlich, es nicht funktioniert, wie es will Mitarbeiter in der Gruppe durch Klausel als auch):
SELECT dept, emp, MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) FROM mytable GROUP BY dept
Lösung
Ich denke, dass die Rang () Funktion ist nicht der Weg mit diesem zu gehen, aus zwei Gründen.
Zum einen ist es wahrscheinlich weniger effizient als ein Min () -. Basierte Methode
Der Grund hierfür ist, dass die Abfrage eine geordnete Liste aller Gehälter pro Abteilung zu halten hat, wie es die Daten durchsucht, und der Rang wird dann später durch erneute Lesen dieser Liste zugeordnet werden. Offensichtlich in Abwesenheit von Indizes, die für diese genutzt werden können, können Sie nicht einen Rang zuweisen, bis der letzte Datenpunkt gelesen wurde, und die Wartung der Liste ist teuer.
So ist die Leistung des Rank () Funktion ist abhängig von der Gesamtzahl der Elemente gescannt wird, und wenn die Anzahl ausreichend ist, dass die Art auf der Festplatte schwappt dann wird die Leistung reduzieren.
Dies ist wahrscheinlich effizienter:
select dept,
emp,
salary
from
(
SELECT dept,
emp,
salary,
Min(salary) Over (Partition By dept) min_salary
FROM mytable
)
where salary = min_salary
/
Diese Methode erfordert nur, dass die Abfrage einen einzelnen Wert pro Abteilung des Minimalwertes bisher begegnet halten. Wenn ein neues Minimum auftritt, ist dann der vorhandene Wert geändert wird, sonst wird der neue Wert verworfen. Die Gesamtzahl der Elemente, die im Speicher gehalten werden müssen ist auf die Anzahl der Abteilungen, nicht in der Anzahl der Zeilen abgetastet werden.
Es könnte sein, dass Oracle einen Codepfad zu erkennen hat, dass der Rang nicht wirklich in diesem Fall berechnet werden müssen, aber ich würde nicht auf sie wetten.
Der zweite Grund für die Abneigung gegen Rank () ist, dass es nur die falsche Frage beantwortet. Die Frage ist nicht „Welche Aufzeichnungen das Gehalt hat, die die erste Ranking ist, wenn die Gehälter pro Abteilung sind aufsteigend geordnet“, es ist „welche Datensätze das Gehalt hat, dass der Mindest pro Abteilung ist“. Das macht einen großen Unterschied für mich zumindest.
Andere Tipps
Ich glaube, Sie ziemlich nah mit Ihrer ursprünglichen Abfrage waren. Im Folgenden würde Ihre Testfall laufen und passen:
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
Im Gegensatz zu den RANK () -Lösungen, diese Garantien höchstens eine Zeile pro Abteilung. Dass aber die Hinweise auf ein Problem: Was in einer Abteilung geschieht, wo es zwei Mitarbeiter auf dem niedrigsten Gehalt? Die RANK () -Lösungen werden beide Mitarbeiter zurückkehren - mehr als eine Zeile für die Abteilung. Diese Antwort wird man willkürlich wählen, und stellen Sie sicher, es gibt nur eine für die Abteilung.
Sie können die RANK()
Syntax. Zum Beispiel wird diese Abfrage Ihnen sagen, wo ein Mitarbeiter Reihen innerhalb ihrer Abteilung in Bezug auf, wie groß ihr Gehalt ist:
SELECT
dept,
emp,
salary,
(RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept
FROM EMPLOYEES
Sie könnten dann Abfrage von diesem, wo 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)