Zurück zur Artikelliste Artikel
9 Leseminuten

Wie man das n-te höchste Gehalt nach Abteilung mit SQL findet

Lernen Sie, wie man das n-te höchste Gehalt in SQL findet, und Sie werden lernen, wie man den n-ten Wert in beliebigen Daten erhält.

In diesem Artikel werde ich mich auf ein Problem konzentrieren und Ihnen mehrere Lösungen für dieses Problem geben. Das Problem ist bereits im Titel genannt: Finde das n-te höchste Gehalt nach Abteilung mit SQL.

Das mag zu spezifisch klingen. Aber wenn Sie lernen, wie man diese Aufgabe löst, können Sie den n-ten Wert in beliebigen Daten finden, nicht nur in Gehaltsdaten. Sie werden eine Vorstellung davon bekommen, wie Sie andere ähnliche Probleme lösen können, die Ihnen begegnen.

Wie ich schon sagte, werden wir nur ein Problem betrachten, aber mehrere Lösungen dafür erforschen. Tatsächlich werde ich Ihnen vier Lösungen zeigen, die diese Fensterfunktionen verwenden:

  • NTH_VALUE()
  • ROW_NUMBER()
  • RANK()
  • DENSE_RANK()

Wenn Sie Hilfe bei der Syntax oder Details zu diesen Funktionen benötigen, können Sie das SQL Fensterfunktionen Cheat Sheet zu Rate ziehen. Sie können auch einen Blick auf dieses Beispiel werfen, um zu sehen, wie Fensterfunktionen im Allgemeinen funktionieren.

Die zu verwendenden Daten

Wir werden Daten verwenden, die in den folgenden zwei Tabellen gespeichert sind:

  • employee
  • department

So sehen die Daten in der Tabelle aus employee aussehen:

idfirst_namelast_namesalarydepartment_id
1GoraudTomankiewicz7,231.061
2HarwellWinny8,139.511
3BastienGoosnell4,574.201
4ZachariahRapi6,657.111
5GiustinoCruikshank5,555.631
6AbraClemon5,564.253
7HurleeDrance9,790.162
8RozannaMcIlvoray3,201.182
9IveStrathdee9,300.252
10LoisSkain5,371.022
11DeborHolby2,804.293
10HadrianRobatham2,615.783
13DixSowter6,378.123
14LeslieSandle8,805.703
15DagnyRosier2,041.263

Die Tabelle department hat die folgenden Daten:

iddepartment_name
1Research and Development
2Accounting
3Human Resources

Was ist hier die Aufgabe?

Wir wollen das dritthöchste Gehalt nach Abteilung finden. Das bedeutet, dass wir den dritthöchsten Wert nicht insgesamt, sondern innerhalb jeder Untergruppe finden müssen, wobei eine Untergruppe die Gehälter für eine bestimmte Abteilung enthält. Das hilfreichste Werkzeug hierfür sind die Fensterfunktionen.

Hier ist also die erste Lösung mit Hilfe einer Fensterfunktion.

Verwendung von NTH_VALUE()

Der Zweck der Funktion NTH_VALUE() ist es, den Wert der n-ten Zeile im Datensatz zu ermitteln. So können wir sie verwenden, um das dritthöchste Gehalt nach Abteilung zu ermitteln:

SELECT	e.first_name,
		e.last_name,
		d.department_name,
		salary,
		NTH_VALUE (salary, 3) OVER (
PARTITION BY department_name ORDER BY salary DESC
			RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS third_highest_salary
FROM department d JOIN employee e ON d.id = e.department_id;

Dieser Code wählt zunächst einige Spalten aus den Tabellen employee und department. Um NTH_VALUE() zu verwenden, müssen wir die Spalte und den Wert von N angeben. Da wir das dritthöchste salary erhalten wollen, ist die Spalte das Gehalt und N = 3; daher haben wir NTH_VALUE(salary, 3). Damit erhalten wir das dritthöchste Gehalt.

Damit eine Fensterfunktion funktioniert, müssen wir eine OVER() Klausel verwenden. Erinnern Sie sich, wir suchen nach dem dritthöchsten Gehalt in jede Abteilung. Dazu verwenden wir PARTITION BY, um die Daten nach der Spalte department_name aus der Tabelle in Teilmengen aufzuteilen department. Da wir nach dem dritthöchstenhöchste Gehalt suchen, verwenden wir ORDER BY, damit die Fensterfunktionen die Berechnungen vom höchsten zum niedrigsten Gehalt durchführen. Dies ist der Grund für ORDER BY salary DESC im Code. Außerdem benötigen wir die Klausel RANGE, in diesem Fall RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING. Das bedeutet, dass die Fensterfunktion zwischen der ersten und der letzten Zeile der Partition suchen wird.

Um all dies zu erhalten, müssen wir natürlich die beiden Tabellen verbinden.

Hier ist das Ergebnis:

first_namelast_namedepartment_namesalarythird_highest_salary
HurleeDranceAccounting9,790.165,371.02
IveStrathdeeAccounting9,300.255,371.02
LoisSkainAccounting5,371.025,371.02
RozannaMcIlvorayAccounting3,201.185,371.02
LeslieSandleHuman Resources8,805.705,564.25
DixSowterHuman Resources6,378.125,564.25
AbraClemonHuman Resources5,564.255,564.25
DeborHolbyHuman Resources2,804.295,564.25
HadrianRobathamHuman Resources2,615.785,564.25
DagnyRosierHuman Resources2,041.265,564.25
HarwellWinnyResearch and Development8,139.516,657.11
GoraudTomankiewiczResearch and Development7,231.066,657.11
ZachariahRapiResearch and Development6,657.116,657.11
GiustinoCruikshankResearch and Development5,555.636,657.11
BastienGoosnellResearch and Development4,574.206,657.11

Das Ergebnis zeigt uns, dass das dritthöchste Gehalt in der Buchhaltung 5.371,02 beträgt, das ist das Gehalt von Lois Skain. Abra Clemons Gehalt (5.564,25) ist das dritthöchste Gehalt in der Personalabteilung. Im Bereich Forschung und Entwicklung ist das Gehalt von Zachariah Rapi das dritthöchste (6.657,11).

Verwendung von ROW_NUMBER()

Die zweite Möglichkeit, das dritthöchste Gehalt nach Abteilung zu ermitteln, ist die Verwendung von ROW_NUMBER(). Diese Fensterfunktion gibt die laufenden Nummern der Zeilen in einem Datensatz zurück. Wenn wir die Gehälter innerhalb jeder Abteilung ordnen, ist es einfach, das dritthöchste Gehalt zu ermitteln. So sieht die Lösung aus:

SELECT	e.first_name,
		e.last_name,
		d.department_name,
		salary,
		ROW_NUMBER() OVER (
PARTITION BY d.id ORDER BY salary DESC
) AS salary_rank
FROM department d JOIN employee e ON d.id = e.department_id
ORDER BY department_name;

Bei dieser Lösung werden die gleichen Spalten ausgewählt wie bei der vorherigen Lösung. Der Unterschied besteht darin, dass wir hier ROW_NUMBER() anstelle von NTH_VALUE() verwenden. Alle Kriterien in der Klausel OVER() sind dieselben wie in der vorherigen Lösung.

Das Ergebnis sieht wie folgt aus:

first_namelast_namedepartment_namesalarysalary_rank
HurleeDranceAccounting9,790.161
IveStrathdeeAccounting9,300.252
LoisSkainAccounting5,371.023
RozannaMcIlvorayAccounting3,201.184
LeslieSandleHuman Resources8,805.701
DixSowterHuman Resources6,378.122
AbraClemonHuman Resources5,564.253
DeborHolbyHuman Resources2,804.294
HadrianRobathamHuman Resources2,615.785
DagnyRosierHuman Resources2,041.266
HarwellWinnyResearch and Development8,139.511
GoraudTomankiewiczResearch and Development7,231.062
ZachariahRapiResearch and Development6,657.113
GiustinoCruikshankResearch and Development5,555.634
BastienGoosnellResearch and Development4,574.205

Es sieht ein wenig anders aus als das Ergebnis der vorherigen Lösung. Die letzte Spalte enthält nicht den Wert für das dritthöchste Gehalt. Stattdessen enthält sie eine Rangfolge, und man kann leicht erkennen, dass der Effekt derselbe ist. Die Werte des dritthöchsten Gehalts nach Abteilung sind 5.371,02 (Buchhaltung), 5.564,25 (Personalwesen) und 6.657,11 (Forschung und Entwicklung), was dem Ergebnis der ersten Lösung entspricht.

Wir können auch ROW_NUMBER() in einem CTE verwenden. Wenn Sie mit CTEs nicht vertraut sind, wird in diesem Artikel erklärt, was das ist. Wir können zum Beispiel den folgenden Code schreiben:

WITH salaries_ranks AS (
SELECT e.first_name,
 e.last_name,
 d.department_name,
 salary,
 ROW_NUMBER() OVER (PARTITION BY d.id ORDER BY salary DESC
 ) AS salary_rank
FROM department d JOIN employee e ON d.id = e.department_id
)

SELECT *
FROM salaries_ranks
WHERE salary_rank = 3;

Wir beginnen unser CTE mit einem WITH, gefolgt vom CTE-Namen, salaries_ranks. Dann schreiben wir eine SELECT Anweisung, die genau die gleiche ist wie im vorherigen Code.

Nun, da wir eine CTE geschrieben haben, können wir sie wie jede andere Tabelle verwenden. Wir tun dies in einer separaten SELECT Anweisung, die alle Spalten aus der CTE auswählt salaries_ranksauswählt und nur die Zeilen anzeigt, in denen salary_rank = 3 steht.

Das Ergebnis dieses Codes ist:

first_namelast_namedepartment_namesalarysalary_rank
ZachariahRapiResearch and Development6,657.113
LoisSkainAccounting5,371.023
AbraClemonHuman Resources5,564.253

Dies ist eine recht elegante Lösung. Wir erhalten nur die notwendigen Daten - nur drei Zeilen mit dem dritthöchsten Gehalt für jede Abteilung.

Verwendung von RANK()

Die dritte Möglichkeit besteht darin, die Funktion RANK() zu verwenden. Sie ähnelt ROW_NUMBER() insofern, als sie auch die Zeilen innerhalb einer Partition ordnet. Ähnlich, aber nicht dasselbe. Ich werde später auf die Unterschiede eingehen, oder Sie können sie hier nachlesen.

Lassen Sie uns den Code mit RANK() schreiben:

SELECT	e.first_name,
		e.last_name,
		d.department_name,
		salary,
		RANK() OVER (
PARTITION BY d.department_name ORDER BY salary DESC
) AS salary_rank
FROM department d JOIN employee e ON d.id = e.department_id;

Auch dieser Code unterscheidet sich nicht wesentlich von den vorherigen, mit Ausnahme der verwendeten Fensterfunktion. Hier ist es RANK(), mit den gleichen Kriterien in der OVER() Klausel. Das Ergebnis ist genau dasselbe wie bei der Verwendung von ROW_NUMBER(), so dass ich nicht glaube, dass es notwendig ist, die gleiche Ergebnistabelle noch einmal zu zeigen.

Verwendung von DENSE_RANK()

Die letzte Lösung, die ich Ihnen hier zeigen möchte, ist die Fensterfunktion DENSE_RANK(). Genau wie ROW_NUMBER() und RANK() ordnet sie die Werte innerhalb eines Datensatzes.

Der Code ist auch nicht wirklich anders. Es ist derselbe wie zuvor, nur unter Verwendung einer anderen Fensterfunktion:

SELECT	e.first_name,
		e.last_name,
		d.department_name,
		salary,
		DENSE_RANK() OVER (
PARTITION BY d.department_name ORDER BY salary DESC 
		) AS salary_rank
FROM department d JOIN employee e ON d.id = e.department_id;

Wenn wir diesen Code ausführen, erhalten wir genau das gleiche Ergebnis wie bei den beiden vorherigen Lösungen.

Überblick über die Konzepte und ihre Unterschiede

Wie Sie gesehen haben, können Sie jede dieser vier Fensterfunktionen verwenden, um das dritthöchste Gehalt nach Abteilung zu ermitteln.

Die Funktion NTH_VALUE() zeigt Ihnen explizit den Wert des dritthöchsten Gehalts nach Abteilung an.

Die Funktionen ROW_NUMBER(), RANK() und DENSE_RANK() ordnen die Gehälter innerhalb jeder Abteilung. Sie können dann einfach den Gehaltswert finden, der dem Rang 3 entspricht. Diese drei Funktionen sind ähnlich, aber nicht identisch. In diesem Beispiel scheinen sie identisch zu sein, aber andere Daten können ihre Unterschiede deutlich machen.

Um Ihnen unangenehme Überraschungen zu ersparen, wird hier erklärt, wie sie sich unterscheiden.

Die Unterschiede kommen zum Vorschein, wenn Sie in Ihren Daten Verknüpfungen (mehrere Zeilen mit demselben Wert) haben. Da ich wusste, dass meine Daten keine Verknüpfungen enthielten, würde ich mit allen drei Funktionen die gleiche Lösung erhalten.

Aber was ist, wenn es doch Verknüpfungen gibt? In diesem Fall würde ROW_NUMBER() die Ränge sequentiell zuweisen, d. h. die Verknüpfungen hätten unterschiedliche Rangnummern, die willkürlich zugewiesen werden. Im Gegensatz dazu weist RANK() den gleichen Werten die gleiche Rangnummer zu und überspringt eine Rangnummer, um sie zu kompensieren, wenn es zu einer Zeile mit einem anderen Wert kommt. DENSE_RANK() schließlich weist Gleichständen die gleiche Rangnummer zu, ohne eine Rangnummer beim nächsten Wert zu überspringen. Hier ein Beispiel zur Veranschaulichung:

employeesalaryrow_number()rank()dense_rank()
12,000111
24,000333
33,000222
48,000554
54,000433

Wenn Sie nach dem Wert des dritthöchsten Gehalts in diesen Daten suchen, liefert ROW_NUMBER() die richtige Lösung: Das Gehalt von 4.000 ist das dritthöchste. Wenn Sie jedoch nach allen Arbeitnehmern mit dem dritthöchsten Gehalt suchen, erhalten Sie eine falsche Lösung. Es würde nur Angestellter 2 angezeigt, obwohl auch Angestellter 5 angezeigt werden sollte. In diesem Fall wäre RANK() oder DENSE_RANK() die bessere Wahl.

Was ist, wenn Sie nach dem Wert des vierthöchsten Gehalts suchen? Die Funktion ROW_NUMBER() würde Ihnen eine völlig falsche Lösung liefern, da der dritthöchste und der vierthöchste Wert identisch sind. Die Funktion RANK() liefert überhaupt kein Ergebnis, da sie den vierten Rang überspringt - wie bereits erläutert, wird bei Unentschieden die gleiche Rangnummer vergeben und die nächste Rangnummer übersprungen, um dies auszugleichen. Nur DENSE_RANK() liefert Ihnen in diesem Fall eine korrekte Lösung.

Bevor Sie sich für eine Funktion entscheiden, sollten Sie sich über die Daten im Klaren sein und wissen, was Sie als Lösung erhalten möchten. Der sicherste Weg ist die Verwendung von NTH_VALUE(). Wenn Sie eine Ranking-Funktion verwenden möchten, ist es im Allgemeinen am besten, DENSE_RANK() zu benutzen, wenn Sie nicht wissen, ob es in den Daten irgendwelche Gleichheiten gibt. Wenn Sie stattdessen ROW_NUMBER() oder RANK() verwenden, stellen Sie sicher, dass Sie wissen, wie das Ergebnis aussehen würde. Vielleicht gibt Ihnen eine oder beide dieser Funktionen, was Sie brauchen. Wählen Sie die Funktion entsprechend Ihren Bedürfnissen.

Sie können diese vier Funktionen auch verwenden, wenn Sie das Höchst- oder Mindestgehalt nach Abteilung ermitteln möchten. Oder, zum Beispiel, das teuerste oder das billigste Produkt nach Produktkategorie. Oder ein beliebiges Produkt mit dem n-ten höchsten Preis. Sie können auch nach dem n-ten höchsten oder dem n-ten niedrigsten Umsatz, Ertrag, Arbeitsstunden, Kosten, Anzahl der Likes, Logins, Engagements, Streams, Kommentare usw. suchen, was auch immer.

Wenn Sie MySQL verwenden, wird diese umfassende Einführung Ihnen diese vier und alle anderen Fensterfunktionen in MySQL zeigen.

Verschiedene Möglichkeiten, den n-ten SQL-Wert zu ermitteln

Die Funktion NTH_VALUE() eignet sich hervorragend, um das n-te höchste Gehalt oder den n-ten Wert einer beliebigen anderen Spalte zu ermitteln. Schließlich wurde sie genau für diesen Zweck entwickelt. Ich habe Ihnen gezeigt, wie Sie den gleichen Effekt mit drei weiteren Fensterfunktionen erzielen können: ROW_NUMBER(), RANK(), und DENSE_RANK(). Verwenden Sie die Funktion, die am besten zu Ihren Bedürfnissen und Daten passt.

Aber das sind nur vier Fensterfunktionen. Das ist nur die Spitze des Eisbergs, und es gibt noch so viel mehr! Diese und andere Fensterfunktionen werden in diesem Kurs über Fensterfunktionen erklärt.