Zum Inhalt springen

~/teaching · data analytics · sitzung 3, donnerstag, 7. januar 2027

SQL-Grundlagen für Führungskräfte: Abfragen lesen, Joins prüfen und von KI geschriebenes SQL kontrollieren

Beim Lesen mit FROM beginnen, fragen, was eine Zeile ist, und vor und nach jedem Join zählen.

Zusammenfassung

Dies ist die dritte Sitzung von Data Analytics for Business, einem Kurs des M2 International Management an der Université Catholique de Lille (2026-2027). Sie folgt auf die Sitzung zu Diagrammen und Storytelling und arbeitet am selben Fall, der offenen Bestellhistorie von Olist, einem brasilianischen Online-Marktplatz. Diese wird nun direkt in DuckDB abgefragt, einer kostenlosen Datenbank-Engine, die im Browser läuft.

Ziel ist nicht, aus Führungskräften Analysten zu machen. Sie sollen eine Abfrage (query) lesen, beurteilen, ob sie die gestellte Frage beantwortet, und eine Zahl prüfen können, bevor sie auf einer Folie mit ihrem Namen landet. Die Sitzung führt von Tabellen und Schlüsseln zu den sechs Klauseln einer Abfrage, dann zu den Joins, wo die meisten stillen Fehler entstehen, und endet mit einer Gewohnheit, die jeden Monat wichtiger wird: von einem KI-Assistenten geschriebenes SQL zu prüfen, bevor man seinem Ergebnis vertraut.

Nach der Sitzung können Sie SELECT, WHERE, GROUP BY und JOIN über mehrere Tabellen lesen und schreiben, erklären, wie ein Join unbemerkt Zeilen verdoppeln oder verlieren kann, und die wenigen Prüfungen durchführen, die das aufdecken. Der nächste Schritt, am Nachmittag, ist prädiktive Analytik für Entscheidungsträger.

Die Folien

SQL-Grundlagen für Führungskräfte

Sitzung 3, Donnerstag, 7. Januar 2027. Data Analytics for Business, Université Catholique de Lille, M2 International Management, 2026-2027. Die Folien dieses Kurses sind auf Englisch.

Folien öffnen (Passwort vom Dozenten)

Der Foliensatz selbst ist für die Studierenden des Kurses verschlüsselt; diese Seite fasst ihn für alle zusammen.

Die Kursseite

Aufbau des Foliensatzes

  1. Willkommen zurück und warum SQL. Eine kurze Wiederholung der früheren Sitzungen, dann vier Gründe, warum eine Führungskraft eine Abfrage lesen können sollte.
  2. Wie die Tabellen zusammenpassen. Primär- und Fremdschlüssel, was eine Zeile jeder Tabelle darstellt, und eine Falle, die in der Kundentabelle steckt.
  3. Live-Demo in DuckDB. Von der Pivot-Tabelle zur Abfrage, dann drei Fragen des Falls, live eingetippt und ausgeführt.
  4. Sie sind dran. Drei Übungen zu zweit, vom Lückentext bis zu einer Rangliste über drei Tabellen, ohne KI-Assistenten.
  5. Joins und wie sie lügen. INNER und LEFT JOIN, Zeilen, die sich vervielfachen, Zeilen, die verschwinden, und die Zählung, die beides aufdeckt.
  6. Finden Sie den Fehler. Abfragen von der Art, wie ein Assistent sie schreibt, jede mit einem klassischen Fehler, und eine Checkliste in vier Schritten.
  7. Abschluss. Vier Gewohnheiten zum Mitnehmen und eine geprüfte SQL-Erkenntnis für den Bericht der Gruppe.

Warum eine Führungskraft SQL liest

Die meisten betrieblichen Systeme speichern ihre Daten in Tabellen, und SQL ist die Sprache, die diesen Tabellen präzise Fragen stellt.

Umsätze, Lagerbestände, Lieferungen und Personaldaten liegen meist in relationalen Tabellen. Eine Führungskraft muss SQL nicht fließend schreiben, aber es lesen zu können, zahlt sich auf vier Arten aus. Sie können eine Zahl prüfen, bevor Sie sie präsentieren. Sie können präzise fragen: welche Zeilen, welcher Filter, wie gezählt. Sie müssen für eine einfache Zählung nicht in der Warteschlange eines Analysten stehen. Und Sie können prüfen, was ein KI-Assistent schreibt, denn Assistenten erzeugen inzwischen in Sekunden plausibles SQL.

Der letzte Grund prägt die ganze Sitzung. Eine Abfrage, die läuft, ist nicht unbedingt eine Abfrage, die stimmt: Die gefährlichen Fehler liefern ein Ergebnis, das vernünftig aussieht. Wer lernt, eine Abfrage zu lesen, und zwar von der richtigen Klausel aus, kann diese Fehler bemerken.

Bemerkung (Das Werkzeug). Die Sitzung nutzt DuckDB im Browser (shell.duckdb.org): nichts zu installieren, ein Einrichtungsskript erstellt die neun Tabellen des Falls, und SHOW TABLES, DESCRIBE und LIMIT 5 genügen, um sich umzusehen, bevor man irgendetwas schreibt.

Zurück zum Inhalt.

Schlüssel und Granularität: wie die Tabellen zusammenpassen

Bevor Sie irgendetwas zählen, müssen Sie wissen, wofür eine Zeile jeder Tabelle steht und welche Spalte sie mit den anderen verbindet.

Ein Primärschlüssel (primary key) identifiziert jede Zeile einer Tabelle genau einmal, etwa order_id in der Bestelltabelle. Ein Fremdschlüssel (foreign key) ist eine Spalte, die auf eine Zeile einer anderen Tabelle verweist, etwa die Kundenkennung auf jeder Bestellung. Ein Join tut nichts anderes, als diesem Verweis zu folgen.

Die Granularität (grain) einer Tabelle ist das, was eine Zeile darstellt. Im Olist-Fall hat orders eine Zeile pro Bestellung und steht im Zentrum des Schemas. Drei Tabellen können mehrere Zeilen für dieselbe Bestellung enthalten: Artikel, Zahlungen und Bewertungen. Genau bei diesen Tabellen gehen Joins schief. Die Geolokalisierungstabelle wird über ein Postleitzahlpräfix verknüpft, das in vielen Zeilen vorkommt, deshalb muss sie vor jedem Join auf eine Zeile pro Präfix reduziert werden.

Der Foliensatz weist auf eine Falle in der Kundentabelle hin: In diesem Datensatz identifiziert die auf einer Bestellung gespeicherte Kundenkennung den Kundendatensatz dieser Bestellung, während die Person hinter mehreren Bestellungen eine eigene Kennung hat. Sobald sich die Frage auf Personen bezieht, prüfen Sie, welche Spalte tatsächlich eine Person bezeichnet.

Tabelle 1: Granularität der wichtigsten Tabellen des Falls
TabelleEine Zeile istZeilen pro Bestellung
orderseine Bestellunggenau eine
customersder Kundendatensatz einer Bestellunggenau eine
order_itemsein Artikel einer Bestellungeine oder mehrere
paymentseine Zahlung einer Bestellungeine oder mehrere
reviewseine Bewertung einer Bestellungkeine, eine oder mehrere

Zurück zum Inhalt.

Anatomie einer Abfrage und die Reihenfolge, in der sie wirklich abläuft

Eine Abfrage ist eine als Text aufgeschriebene Pivot-Tabelle: Tabelle wählen, Zeilen filtern, gruppieren, messen, sortieren.

Jede Entscheidung, die man in einer Pivot-Tabelle (pivot table) per Mausklick trifft, hat in SQL einen Namen. Die Quelldaten sind FROM, der Filter ist WHERE, die Zeilen der Pivot-Tabelle sind GROUP BY, die Werte sind Aggregatfunktionen (aggregates) wie COUNT, SUM oder AVG in SELECT, und die Sortierung ist ORDER BY. Der Unterschied: SQL hält jede Entscheidung als Text fest, den eine Kollegin im nächsten Monat erneut ausführen und Zeile für Zeile lesen kann, während eine per Mausklick gebaute Pivot-Tabelle keine Spur hinterlässt, die sich prüfen ließe.

Eine Abfrage hat bis zu sechs Klauseln, immer in derselben Reihenfolge geschrieben. Nur SELECT und FROM sind Pflicht. Textwerte stehen in einfachen Anführungszeichen; in doppelten Anführungszeichen sucht DuckDB nach einer Spalte dieses Namens. Hier ein Beispiel im Geist der Demos, eigens für diese Zusammenfassung geschrieben.

SELECT s.seller_state,
       COUNT(*) AS n_items,
       ROUND(AVG(i.freight_value), 2) AS avg_freight
FROM order_items AS i
JOIN sellers AS s ON s.seller_id = i.seller_id
WHERE i.price >= 100
GROUP BY s.seller_state
ORDER BY avg_freight DESC
LIMIT 5;

So lesen Sie sie: in der Reihenfolge, in der die Datenbank arbeitet, nicht in der Schreibreihenfolge

  1. FROM und die Joins. Die Bestellartikel nehmen und jedem den Verkäufer zuordnen, von dem er stammt. Das entscheidet, welche Zeilen existieren.
  2. WHERE. Nur die Artikel mit einem Preis von 100 oder mehr behalten.
  3. GROUP BY. Eine Gruppe pro Bundesstaat des Verkäufers bilden.
  4. SELECT. In jeder Gruppe die Zeilen zählen und die Frachtkosten mitteln.
  5. ORDER BY, dann LIMIT. Nach diesem Mittelwert sortieren, den größten zuerst, und fünf Zeilen behalten.

Diese logische Reihenfolge ist das zentrale Leseprinzip des Foliensatzes: Um die Abfrage eines anderen zu verstehen, vor allem eine von einem Assistenten geschriebene, beginnen Sie bei FROM und den Joins, nie bei SELECT. Die Engine kann die tatsächliche Arbeit anders organisieren, aber das Ergebnis verhält sich so, als folgte es dieser Reihenfolge.

Die Live-Demos beantworten dann drei Fragen des Falls: Bestellungen nach Status, verspätete Lieferungen unter den 2017 aufgegebenen Bestellungen und Bestellungen nach Bundesstaat des Kunden. Die zweite schreibt die Verspätungsregel aus der ersten Sitzung mit CASE WHEN neu, das jeder Zeile ein Label zuweist, und mit EXTRACT, das das Jahr aus einem Datum herauszieht. Sie filtert sowohl nach dem Status als auch nach dem Vorhandensein eines Lieferdatums, denn das sind zwei getrennte Spalten, und nichts garantiert, dass sie übereinstimmen. Die dritte ist ein erster Join: Der Bundesstaat steht in customers, nicht in orders.

Zurück zum Inhalt.

INNER JOIN und LEFT JOIN: Die Frage entscheidet

Ein Inner Join behält nur Zeilen, die einen Partner finden; ein Left Join hält alle Zeilen der ersten Tabelle fest, mit oder ohne Partner.

Ein einfaches JOIN ist ein INNER JOIN: Eine Zeile bleibt nur erhalten, wenn sie auf beiden Seiten eine Entsprechung findet. Ein LEFT JOIN behält alle Zeilen der ersten Tabelle, also der nach FROM genannten, und füllt die Spalten der anderen Tabelle mit NULL, wenn es keine Entsprechung gibt. Keiner der beiden ist grundsätzlich besser. Eine Durchschnittsbewertung über bewertete Bestellungen lässt sich gut mit einem Inner Join berechnen; der Anteil der Bestellungen mit mindestens einer Bewertung braucht einen Left Join, sonst gelangen die unbewerteten Bestellungen nie in den Nenner.

Tabelle 2: Daten zur Veranschaulichung aus dem Foliensatz: drei Bestellungen, Bewertungen für zwei davon
BestellungHat eine BewertungINNER JOINLEFT JOIN
o1jabehaltenbehalten
o2neinentferntbehalten, Bewertungsspalten NULL
o3jabehaltenbehalten
Zurückgegebene Zeilen23

Eine zweite, subtilere Art, Zeilen zu verlieren, betrifft Filter. Nach einem Left Join wird eine Bedingung auf die rechte Tabelle, die in WHERE steht, für jede Zeile geprüft; ein fehlender Wert (NULL) besteht keinen Vergleich, und die Zeilen ohne Partner verschwinden: Der Left Join ist unbemerkt zu einem Inner Join geworden. In ON platziert, entscheidet dieselbe Bedingung nur darüber, welche Partnerzeilen angehängt werden, und jede Zeile der ersten Tabelle bleibt im Ergebnis.

Bemerkung (Faustregel). Bei einem LEFT JOIN gehören Bedingungen auf die rechte Tabelle in ON, nicht in WHERE.

Zurück zum Inhalt.

Wenn ein Join Zeilen vervielfacht

Verknüpft man zwei Tabellen, die jeweils mehrere Zeilen pro Bestellung haben, wird jede Zeile mit jeder anderen gepaart, und Summen blähen sich unbemerkt auf.

Nehmen Sie eine Bestellung mit zwei Artikeln und zwei Zahlungen und verknüpfen Sie Artikel und Zahlungen über die Bestellkennung. Die Engine paart jeden Artikel mit jeder Zahlung, die Bestellung belegt nun also vier Zeilen, und jede Zahlung erscheint zweimal. Jede Summe der Zahlungen über diese Zeilen ist zu hoch, und nichts warnt Sie. Das ist die Zeilenvervielfachung (fan-out).

nZeilen=nArtikel×nZahlungen=2×2=4n_{\text{Zeilen}} = n_{\text{Artikel}} \times n_{\text{Zahlungen}} = 2 \times 2 = 4
(1)

Der Fehler ist schwer zu sehen, weil die meisten Bestellungen einen Artikel und eine Zahlung haben, sodass nur ein Teil der Summe aufgebläht ist. Das Ergebnis ist falsch, ohne absurd zu sein, und genau deshalb landen solche Zahlen in Berichten.

Vorher zählen, nachher zählen

Die einfachste Prüfung der Sitzung dauert zehn Sekunden. In der Bestelltabelle ist die Zahl der Zeilen gleich der Zahl der verschiedenen Bestellungen. Führen Sie dieselben zwei Zählungen nach dem Join aus und vergleichen Sie.

SELECT COUNT(*) AS n_rows,
       COUNT(DISTINCT o.order_id) AS n_orders
FROM orders AS o
JOIN payments AS p ON p.order_id = o.order_id;
Tabelle 3: Die beiden Zählungen nach einem Join lesen
Was Sie sehenWas es bedeutet
Beide Zählungen unverändertWeiterhin eine Zeile pro Bestellung: Summieren ist sicher
Mehr Zeilen, gleiche Zahl an BestellungenZeilen wurden vervielfacht: Summen sind aufgebläht
Weniger verschiedene BestellungenBestellungen ohne Partner sind weggefallen

Beide Alarme können im selben Join anschlagen. Die Lösung, wenn zwei Tabellen mit mehreren Zeilen pro Bestellung gemeinsam gebraucht werden, lautet erst verdichten, dann verknüpfen: Zuerst jede Tabelle so zusammenfassen, dass jede Bestellung einmal vorkommt, und erst dann diese Zusammenfassungen verknüpfen, die genau zueinander passen. WITH gibt jeder kleinen Abfrage einen Namen, damit sie wie eine Tabelle verwendet werden kann; eine solche benannte Abfrage heißt Common Table Expression (CTE). Eine Führungskraft muss das nicht fließend schreiben, sollte es aber erkennen und es einfordern, wenn Artikel und Zahlungen unverdichtet verknüpft werden.

WITH per_order_items AS (
  SELECT order_id, COUNT(*) AS items
  FROM order_items
  GROUP BY order_id
), per_order_score AS (
  SELECT order_id, AVG(review_score) AS score
  FROM reviews
  GROUP BY order_id
)
SELECT i.items, COUNT(*) AS n_orders, ROUND(AVG(s.score), 2) AS avg_score
FROM per_order_items AS i
JOIN per_order_score AS s ON s.order_id = i.order_id
GROUP BY i.items
ORDER BY i.items;

Zurück zum Inhalt.

Von einem KI-Assistenten geschriebenes SQL prüfen

Folgenschwere Fehler hindern eine Abfrage selten daran zu laufen, deshalb muss ein Ergebnis von einem Assistenten geprüft und nicht nur ausgeführt werden.

Der letzte praktische Teil zeigt drei kurze Abfragen von der Art, wie ein Assistent sie aus einer vagen Anfrage schreibt, und jede verbirgt einen klassischen Fehler. Zu zweit sagen die Studierenden, welche Zeile falsch ist und ob der Fehler das Ergebnis nach oben oder nach unten verschiebt; die Richtung festzulegen zwingt dazu, den Fehler zu verstehen, statt ihn bloß zu entdecken. Der Foliensatz verallgemeinert die Lektion dann zu vier Prüfungen, die in dieser Reihenfolge durchzuführen sind.

Vier Prüfungen, bevor Sie einer Abfrage vertrauen

  1. Granularität. Was ist eine Zeile vor und nach jedem Join? Vergleichen Sie die Zeilenzahl mit der Zahl verschiedener Schlüssel.
  2. Fehlende Werte und Filter. Sehen Sie sich leere Datumsfelder, den Status der Datensätze und den abgedeckten Zeitraum an; geben Sie fehlenden Werten ein eigenes Label, damit Sie sehen, wie viele es sind.
  3. Eine Summe, die Sie schon kennen. Berechnen Sie eine Summe mit so wenigen Tabellen wie möglich neu; weicht die komplexe Abfrage ab, halten Sie inne und finden Sie heraus, warum.
  4. Erklären lassen. Bitten Sie den Assistenten, jede Zeile zu erklären und seine Annahmen zu nennen, und überprüfen Sie diese Aussagen dann in den Daten, denn eine Erklärung ist kein Beweis.

Bemerkung (Die Daten schützen). Beschreiben Sie die Tabellen und fügen Sie die Abfrage ein, niemals die Daten. Echte Kundendatensätze gehören nicht in einen Prompt.

Zum Abschluss der Sitzung soll jede Gruppe ihrem Bericht eine Erkenntnis hinzufügen, die eine Abfrage besser beantwortet als eine Pivot-Tabelle, in einem Satz mit ihrer Zahl und ihrem Geltungsbereich formuliert, und die Abfrage samt ihren Zeilenzählungen aufbewahren, denn irgendjemand wird fragen, wie die Zahl zustande kam.

Zurück zum Inhalt.

Das Wichtigste in Kürze

  1. Lesen Sie eine Abfrage von FROM und ihren Joins aus: Sie legen die Menge der Zeilen fest, bevor irgendetwas gezählt wird.
  2. Fragen Sie vor dem Zählen, was eine Zeile ist: Ein Artikel ist keine Bestellung, und ein Datensatz ist nicht unbedingt eine Person.
  3. Zählen Sie Zeilen und verschiedene Schlüssel vor und nach jedem Join; mehr Zeilen bedeuten aufgeblähte Summen, weniger Schlüssel verlorene Datensätze.
  4. Verdichten Sie Tabellen mit mehreren Zeilen pro Schlüssel auf eine Zeile pro Schlüssel, bevor Sie sie miteinander verknüpfen.
  5. Setzen Sie bei einem LEFT JOIN Bedingungen auf die rechte Tabelle in ON, nicht in WHERE.
  6. Eine Abfrage, die läuft, ist noch keine Abfrage, die stimmt: Prüfen Sie von KI geschriebenes SQL an einer Summe, die Sie bereits kennen.

Glossar

Primärschlüssel (primary key)
Spalte, deren Wert jede Zeile einer Tabelle genau einmal identifiziert.
Fremdschlüssel (foreign key)
Spalte, die auf eine Zeile einer anderen Tabelle verweist; der Weg, dem ein Join folgt.
Granularität (grain)
Was eine Zeile einer Tabelle oder eines Ergebnisses darstellt: eine Bestellung, einen Artikel, eine Zahlung.
Abfrage (query)
Eine in SQL als Text geschriebene Frage an eine Datenbank, die jeder erneut ausführen und lesen kann.
Aggregatfunktionen (aggregates)
Funktionen wie COUNT, SUM oder AVG, die die Zeilen einer Gruppe zu einem einzigen Wert verdichten.
INNER JOIN
Ein Join, der nur die Zeilen mit einer Entsprechung auf beiden Seiten behält.
LEFT JOIN
Ein Join, der alle Zeilen der ersten Tabelle behält und fehlende Entsprechungen mit NULL füllt.
fehlender Wert (NULL)
Das Fehlen eines Werts; jeder Vergleich damit ergibt „unbekannt“ statt wahr oder falsch.
Zeilenvervielfachung (fan-out)
Die Vervielfachung von Zeilen, wenn ein Join auf beiden Seiten auf mehrere passende Zeilen trifft.
Common Table Expression (CTE)
Eine benannte Unterabfrage, die mit WITH deklariert und dann wie eine Tabelle verwendet wird.
Pivot-Tabelle (pivot table)
Per Mausklick gebaute Zusammenfassung in einer Tabellenkalkulation; SQL schreibt dieselben Entscheidungen als Text auf.

Die anderen Notizen des Kurses

Eine öffentliche Zusammenfassung pro Foliensatz, in der Reihenfolge des Kurses.

  1. Sitzung 1Von Daten zu Entscheidungen
  2. Sitzung 2Datenvisualisierung und Storytelling
  3. Sitzung 3SQL-Grundlagen für Führungskräfte
  4. Sitzung 4Prädiktive Analytik für Entscheidungsträger
  5. Sitzung 5Generative KI, DSGVO und AI Act
  6. Sitzung 6Datenerkenntnisse präsentieren

Alle Notizen als Leseliste

Über diese Notizen

Vom Dozenten auf Grundlage der Folien verfasst, als öffentliche Zusammenfassung: die Kernideen, die Formeln und ein durchgerechnetes Beispiel. Die Übungen, ihre Lösungen, die Tests und die Bewertung bleiben im Foliensatz, für die Studierenden des Kurses.

Stand: .