MySQL-Fensterfunktionen (Window Functions)

MySQLMySQLBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab werden wir die Macht der MySQL-Fensterfunktionen (Window Functions) erkunden. Das Ziel besteht darin, zu lernen, wie man Berechnungen über Sätze von Tabellenzeilen durchführt, die mit der aktuellen Zeile in Beziehung stehen.

Wir beginnen damit, jeder Zeile mithilfe von ROW_NUMBER() eine eindeutige Zeilennummer zuzuweisen und die Mitarbeiter nach ihrem Gehalt zu sortieren. Anschließend berechnen wir eine laufende Summe mit SUM OVER, verwenden PARTITION BY für gruppierte Berechnungen und vergleichen schließlich Zeilen mit LAG. In diesem Lab werden praktische Beispiele bereitgestellt, um diese Fensterfunktionen effektiv zu verstehen und anzuwenden.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL mysql(("MySQL")) -.-> mysql/BasicKeywordsandStatementsGroup(["Basic Keywords and Statements"]) mysql/BasicKeywordsandStatementsGroup -.-> mysql/create_table("Table Creation") mysql/BasicKeywordsandStatementsGroup -.-> mysql/select("Data Retrieval") mysql/BasicKeywordsandStatementsGroup -.-> mysql/insert("Data Insertion") subgraph Lab Skills mysql/create_table -.-> lab-550921{{"MySQL-Fensterfunktionen (Window Functions)"}} mysql/select -.-> lab-550921{{"MySQL-Fensterfunktionen (Window Functions)"}} mysql/insert -.-> lab-550921{{"MySQL-Fensterfunktionen (Window Functions)"}} end

Zuweisen von Zeilennummern mit ROW_NUMBER

In diesem Schritt werden wir untersuchen, wie man die MySQL-Fensterfunktion (Window Function) ROW_NUMBER() verwendet, um jeder Zeile in einem Ergebnisset eine eindeutige aufsteigende Ganzzahl zuzuweisen. Dies ist besonders nützlich, wenn Sie Daten klassifizieren oder paginieren müssen.

Bevor wir uns einem praktischen Beispiel zuwenden, schauen wir uns die grundlegende Syntax von ROW_NUMBER() an:

ROW_NUMBER() OVER (ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...)
  • ROW_NUMBER(): Dies ist die Fensterfunktion selbst.
  • OVER(): Diese Klausel gibt das Fenster an, also die Menge von Zeilen, auf denen die Funktion operiert.
  • ORDER BY: Innerhalb der OVER()-Klausel bestimmt die ORDER BY-Klausel die Reihenfolge, in der die Zeilennummern zugewiesen werden. Sie können eine oder mehrere Spalten zur Sortierung angeben und für jede Spalte die aufsteigende (ASC) oder absteigende (DESC) Reihenfolge auswählen.

Erstellen wir nun eine Beispieltabelle namens employees und fügen einige Daten ein. Öffnen Sie Ihr Terminal und verbinden Sie sich mit Ihrem MySQL-Server über den mysql-Befehlszeilenclient. Möglicherweise müssen Sie Ihren Benutzernamen und Ihr Passwort angeben.

mysql -u root -p

Nachdem Sie verbunden sind, führen Sie die folgenden SQL-Anweisungen aus, um die employees-Tabelle zu erstellen und Daten einzufügen:

CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    employee_name VARCHAR(50),
    department VARCHAR(50),
    salary DECIMAL(10, 2)
);

INSERT INTO employees (employee_id, employee_name, department, salary) VALUES
(1, 'Alice', 'Sales', 60000.00),
(2, 'Bob', 'Marketing', 55000.00),
(3, 'Charlie', 'Sales', 65000.00),
(4, 'David', 'IT', 70000.00),
(5, 'Eve', 'Marketing', 62000.00);

Nun verwenden wir ROW_NUMBER(), um jeder Mitarbeiterin bzw. jedem Mitarbeiter eine Zeilennummer basierend auf ihrem Gehalt in absteigender Reihenfolge zuzuweisen. Führen Sie die folgende Abfrage aus:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM
    employees;

Diese Abfrage gibt das folgende Ergebnis zurück:

+-------------+---------------+------------+----------+---------+
| employee_id | employee_name | department | salary   | row_num |
+-------------+---------------+------------+----------+---------+
|           4 | David         | IT         | 70000.00 |       1 |
|           3 | Charlie       | Sales      | 65000.00 |       2 |
|           5 | Eve           | Marketing  | 62000.00 |       3 |
|           1 | Alice         | Sales      | 60000.00 |       4 |
|           2 | Bob           | Marketing  | 55000.00 |       5 |
+-------------+---------------+------------+----------+---------+
5 rows in set (0.00 sec)

Wie Sie sehen können, enthält die Spalte row_num die Zeilennummer, die jeder Mitarbeiterin bzw. jedem Mitarbeiter basierend auf ihrem Gehalt zugewiesen wurde, wobei der/die Mitarbeiter/in mit dem höchsten Gehalt die Zeilennummer 1 erhält.

Sie können ROW_NUMBER() auch mit anderen ORDER BY-Klauseln verwenden. Beispielsweise können Sie die folgende Abfrage verwenden, um Zeilennummern basierend auf dem Namen der Mitarbeiterinnen und Mitarbeiter in aufsteigender Reihenfolge zuzuweisen:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    ROW_NUMBER() OVER (ORDER BY employee_name ASC) AS row_num
FROM
    employees;

Dies ergibt die folgende Ausgabe:

+-------------+---------------+------------+----------+---------+
| employee_id | employee_name | department | salary   | row_num |
+-------------+---------------+------------+----------+---------+
|           1 | Alice         | Sales      | 60000.00 |       1 |
|           2 | Bob           | Marketing  | 55000.00 |       2 |
|           3 | Charlie       | Sales      | 65000.00 |       3 |
|           4 | David         | IT         | 70000.00 |       4 |
|           5 | Eve           | Marketing  | 62000.00 |       5 |
+-------------+---------------+------------+----------+---------+
5 rows in set (0.00 sec)

Diesmal spiegelt die Spalte row_num die alphabetische Reihenfolge der Namen der Mitarbeiterinnen und Mitarbeiter wider.

Berechnung einer laufenden Summe mit SUM OVER

In diesem Schritt werden wir lernen, wie man eine laufende Summe (Running Total) mithilfe der MySQL-Fensterfunktion (Window Function) SUM() OVER() berechnet. Eine laufende Summe, auch als kumulative Summe bekannt, ist die Summe einer Zahlenfolge, die jedes Mal aktualisiert wird, wenn eine neue Zahl zur Folge hinzugefügt wird. Dies ist nützlich, um den Fortschritt über die Zeit zu verfolgen, kumulative Verkäufe zu berechnen oder Trends zu analysieren.

Die grundlegende Syntax für SUM() OVER() lautet wie folgt:

SUM(expression) OVER (ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...)
  • SUM(expression): Dies berechnet die Summe des angegebenen Ausdrucks.
  • OVER(): Diese Klausel gibt das Fenster an, also die Menge von Zeilen, auf denen die Funktion operiert.
  • ORDER BY: Innerhalb der OVER()-Klausel bestimmt die ORDER BY-Klausel die Reihenfolge, in der die Summe akkumuliert wird.

Wir werden weiterhin die im vorherigen Schritt erstellte Tabelle employees verwenden. Wenn Sie diese noch nicht erstellt haben, lesen Sie bitte den vorherigen Schritt, um die Tabelle zu erstellen und die Daten einzufügen.

Nun berechnen wir die laufende Summe der Gehälter aller Mitarbeiter, sortiert nach ihrer employee_id. Führen Sie die folgende Abfrage aus:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    SUM(salary) OVER (ORDER BY employee_id) AS running_total
FROM
    employees;

Diese Abfrage liefert das folgende Ergebnis:

+-------------+---------------+------------+----------+---------------+
| employee_id | employee_name | department | salary   | running_total |
+-------------+---------------+------------+----------+---------------+
|           1 | Alice         | Sales      | 60000.00 |      60000.00 |
|           2 | Bob           | Marketing  | 55000.00 |     115000.00 |
|           3 | Charlie       | Sales      | 65000.00 |     180000.00 |
|           4 | David         | IT         | 70000.00 |     250000.00 |
|           5 | Eve           | Marketing  | 62000.00 |     312000.00 |
+-------------+---------------+------------+----------+---------------+
5 rows in set (0.00 sec)

Die Spalte running_total zeigt die kumulative Summe der Gehälter, wenn wir die Zeilen nach employee_id sortiert durchgehen. Beispielsweise ist die laufende Summe für Bob (employee_id 2) die Summe aus Alices und Bobs Gehältern (60000 + 55000 = 115000).

Sie können die laufende Summe auch nach anderen Spalten sortieren, wie z.B. nach dem Gehalt. Berechnen wir nun die laufende Summe der Gehälter, sortiert nach dem Gehalt in aufsteigender Reihenfolge:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    SUM(salary) OVER (ORDER BY salary) AS running_total
FROM
    employees;

Die Ausgabe lautet:

+-------------+---------------+------------+----------+---------------+
| employee_id | employee_name | department | salary   | running_total |
+-------------+---------------+------------+----------+---------------+
|           2 | Bob           | Marketing  | 55000.00 |      55000.00 |
|           1 | Alice         | Sales      | 60000.00 |     115000.00 |
|           5 | Eve           | Marketing  | 62000.00 |     177000.00 |
|           3 | Charlie       | Sales      | 65000.00 |     242000.00 |
|           4 | David         | IT         | 70000.00 |     312000.00 |
+-------------+---------------+------------+----------+---------------+
5 rows in set (0.00 sec)

Jetzt wird die running_total basierend auf der aufsteigenden Reihenfolge der Gehälter berechnet.

Verwenden von PARTITION BY für gruppierte Berechnungen

In diesem Schritt werden wir untersuchen, wie man die PARTITION BY-Klausel in MySQL-Fensterfunktionen (Window Functions) verwendet, um Berechnungen für Gruppen von Zeilen durchzuführen. PARTITION BY teilt die Zeilen in Partitionen auf, und die Fensterfunktion wird unabhängig auf jede Partition angewendet. Dies ist nützlich für die Berechnung von Aggregaten innerhalb von Gruppen, die Rangfolge von Elementen innerhalb von Kategorien oder den Vergleich von Werten innerhalb von Segmenten.

Die grundlegende Syntax für die Verwendung von PARTITION BY mit einer Fensterfunktion lautet:

function(expression) OVER (PARTITION BY column1, column2, ... ORDER BY column3 [ASC|DESC], ...)
  • function(expression): Dies ist die Fensterfunktion, die Sie verwenden möchten (z.B. SUM(), AVG(), ROW_NUMBER()).
  • OVER(): Diese Klausel gibt das Fenster an.
  • PARTITION BY: Diese Klausel teilt die Zeilen in Partitionen auf, basierend auf den angegebenen Spalten.
  • ORDER BY: Innerhalb jeder Partition bestimmt die ORDER BY-Klausel die Reihenfolge, in der die Funktion angewendet wird.

Wir werden weiterhin die in den vorherigen Schritten erstellte Tabelle employees verwenden. Wenn Sie diese noch nicht erstellt haben, lesen Sie bitte die vorherigen Schritte, um die Tabelle zu erstellen und die Daten einzufügen.

Berechnen wir nun das durchschnittliche Gehalt für jede Abteilung mit PARTITION BY. Führen Sie die folgende Abfrage aus:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    AVG(salary) OVER (PARTITION BY department) AS avg_salary_by_dept
FROM
    employees;

Diese Abfrage liefert das folgende Ergebnis:

+-------------+---------------+------------+----------+----------------------+
| employee_id | employee_name | department | salary   | avg_salary_by_dept |
+-------------+---------------+------------+----------+----------------------+
|           4 | David         | IT         | 70000.00 |           70000.0000 |
|           2 | Bob           | Marketing  | 55000.00 |           58500.0000 |
|           5 | Eve           | Marketing  | 62000.00 |           58500.0000 |
|           1 | Alice         | Sales      | 60000.00 |           62500.0000 |
|           3 | Charlie       | Sales      | 65000.00 |           62500.0000 |
+-------------+---------------+------------+----------+----------------------+
5 rows in set (0.00 sec)

Die Spalte avg_salary_by_dept zeigt das durchschnittliche Gehalt für die Abteilung jedes Mitarbeiters. Beachten Sie, dass das durchschnittliche Gehalt für alle Mitarbeiter innerhalb derselben Abteilung gleich ist.

Nun kombinieren wir PARTITION BY mit ROW_NUMBER(), um die Mitarbeiter innerhalb jeder Abteilung basierend auf ihrem Gehalt zu klassifizieren. Führen Sie die folgende Abfrage aus:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_by_salary
FROM
    employees;

Diese Abfrage liefert das folgende Ergebnis:

+-------------+---------------+------------+----------+----------------+
| employee_id | employee_name | department | salary   | rank_by_salary |
+-------------+---------------+------------+----------+----------------+
|           4 | David         | IT         | 70000.00 |              1 |
|           5 | Eve           | Marketing  | 62000.00 |              1 |
|           2 | Bob           | Marketing  | 55000.00 |              2 |
|           3 | Charlie       | Sales      | 65000.00 |              1 |
|           1 | Alice         | Sales      | 60000.00 |              2 |
+-------------+---------------+------------+----------+----------------+
5 rows in set (0.00 sec)

Die Spalte rank_by_salary zeigt die Rangfolge jedes Mitarbeiters innerhalb ihrer Abteilung basierend auf ihrem Gehalt, wobei der Mitarbeiter mit dem höchsten Gehalt den Rang 1 erhält. Die Rangfolge beginnt für jede Abteilung neu, aufgrund der PARTITION BY department-Klausel.

Vergleich von Zeilen mit LAG

In diesem Schritt werden wir lernen, wie man die MySQL-Fensterfunktion (Window Function) LAG() verwendet, um auf Daten aus einer vorherigen Zeile im gleichen Ergebnisset zuzugreifen. Dies ist nützlich für den Vergleich von Werten zwischen aufeinanderfolgenden Zeilen, die Berechnung von Differenzen oder die Erkennung von Trends über die Zeit.

Die grundlegende Syntax für LAG() lautet wie folgt:

LAG(expression, offset, default_value) OVER (PARTITION BY column1, column2, ... ORDER BY column3 [ASC|DESC], ...)
  • LAG(expression, offset, default_value): Diese Funktion gibt den Wert des expression aus einer Zeile zurück, die offset Zeilen vor der aktuellen Zeile liegt.
    • expression: Die Spalte oder der Ausdruck, dessen Wert aus der vorherigen Zeile abgerufen werden soll.
    • offset: Die Anzahl der Zeilen, die zurückgesehen werden soll. Wenn weggelassen, ist der Standardwert 1.
    • default_value: Der Wert, der zurückgegeben wird, wenn der offset über den Anfang der Partition hinausgeht. Wenn weggelassen, ist der Standardwert NULL.
  • OVER(): Diese Klausel gibt das Fenster an.
  • PARTITION BY: Diese Klausel teilt die Zeilen in Partitionen auf, basierend auf den angegebenen Spalten.
  • ORDER BY: Innerhalb jeder Partition bestimmt die ORDER BY-Klausel die Reihenfolge, in der die Zeilen verarbeitet werden.

Wir werden weiterhin die in den vorherigen Schritten erstellte Tabelle employees verwenden. Wenn Sie diese noch nicht erstellt haben, lesen Sie bitte die vorherigen Schritte, um die Tabelle zu erstellen und die Daten einzufügen.

Vergleichen wir nun das Gehalt jedes Mitarbeiters mit dem Gehalt des vorherigen Mitarbeiters, sortiert nach employee_id. Führen Sie die folgende Abfrage aus:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    LAG(salary, 1, 0) OVER (ORDER BY employee_id) AS previous_salary
FROM
    employees;

Diese Abfrage liefert das folgende Ergebnis:

+-------------+---------------+------------+----------+-----------------+
| employee_id | employee_name | department | salary   | previous_salary |
+-------------+---------------+------------+----------+-----------------+
|           1 | Alice         | Sales      | 60000.00 |          0.00 |
|           2 | Bob           | Marketing  | 55000.00 |     60000.00 |
|           3 | Charlie       | Sales      | 65000.00 |     55000.00 |
|           4 | David         | IT         | 70000.00 |     65000.00 |
|           5 | Eve           | Marketing  | 62000.00 |     70000.00 |
+-------------+---------------+------------+----------+-----------------+
5 rows in set (0.00 sec)

Die Spalte previous_salary zeigt das Gehalt des vorherigen Mitarbeiters basierend auf employee_id. Für Alice (employee_id 1) ist das previous_salary 0, da es keinen vorherigen Mitarbeiter gibt. Wir haben 0 als default_value in der LAG()-Funktion angegeben.

Nun berechnen wir die Differenz zwischen dem Gehalt jedes Mitarbeiters und dem Gehalt des vorherigen Mitarbeiters. Führen Sie die folgende Abfrage aus:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    salary - LAG(salary, 1, 0) OVER (ORDER BY employee_id) AS salary_difference
FROM
    employees;

Diese Abfrage liefert das folgende Ergebnis:

+-------------+---------------+------------+----------+-------------------+
| employee_id | employee_name | department | salary   | salary_difference |
+-------------+---------------+------------+----------+-------------------+
|           1 | Alice         | Sales      | 60000.00 |         60000.00 |
|           2 | Bob           | Marketing  | 55000.00 |         -5000.00 |
|           3 | Charlie       | Sales      | 65000.00 |         10000.00 |
|           4 | David         | IT         | 70000.00 |          5000.00 |
|           5 | Eve           | Marketing  | 62000.00 |         -8000.00 |
+-------------+---------------+------------+----------+-------------------+
5 rows in set (0.00 sec)

Die Spalte salary_difference zeigt die Differenz zwischen dem Gehalt jedes Mitarbeiters und dem Gehalt des vorherigen Mitarbeiters.

Sie können auch PARTITION BY mit LAG() verwenden. Beispielsweise, wenn Sie eine Tabelle mit Verkaufsdaten für verschiedene Regionen über die Zeit hätten, könnten Sie PARTITION BY region verwenden, um die Verkäufe innerhalb jeder Region zu vergleichen.

Zusammenfassung

In diesem Lab haben wir die Verwendung von MySQL-Fensterfunktionen (Window Functions) untersucht, wobei wir uns speziell auf die Zuweisung von Zeilennummern mit ROW_NUMBER() konzentriert haben. Wir haben gelernt, wie man die OVER()-Klausel mit ORDER BY verwendet, um das Fenster zu definieren und die Reihenfolge festzulegen, in der Zeilennummern innerhalb eines Ergebnisses zugewiesen werden. Dies ermöglicht die Rangfolge und Paginierung von Daten.

Wir haben eine Tabelle employees erstellt und sie mit Beispieldaten befüllt. Anschließend haben wir die ROW_NUMBER()-Funktion angewendet, um jedem Mitarbeiter eine eindeutige aufeinanderfolgende Ganzzahl basierend auf seinem Gehalt in absteigender Reihenfolge zuzuweisen, was die praktische Anwendung dieser Fensterfunktion zeigt.