Abfragen & Kennzahlen
Gezielt lesen
Nur benötigte Felder und eine deterministische Sortierung verwenden.
Beispiel kopieren
SELECT order_id, amount
FROM orders
WHERE status = 'OPEN' AND amount >= 100
ORDER BY amount DESC, order_id;
Ohne ORDER BY ist keine Reihenfolge zugesichert.
Abfragen & Kennzahlen
NULL prüfen
Fehlende Werte getrennt von leeren Zeichenketten betrachten.
Beispiel kopieren
SELECT order_id, COALESCE(reference, 'missing') AS reference
FROM orders
WHERE customer_id IS NULL;
Nicht = NULL verwenden. COALESCE ist eine fachliche Ersatzwertentscheidung.
Abfragen & Kennzahlen
Gruppen filtern
Zeilen vor Aggregation, Gruppen nach Aggregation filtern.
Beispiel kopieren
SELECT customer_id, COUNT(*) AS n, SUM(amount) AS total
FROM orders
WHERE status = 'OPEN'
GROUP BY customer_id
HAVING SUM(amount) > 1000;
COUNT(*) zählt Zeilen; COUNT(spalte) zählt Nicht-NULL-Werte.
Abfragen & Kennzahlen
LEFT JOIN erhalten
Alle Kunden einschließlich derer ohne offene Bestellung anzeigen.
Beispiel kopieren
SELECT c.customer_id, o.order_id
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.customer_id
AND o.status = 'OPEN';
Ein Filter auf o.status im WHERE kann Kunden ohne Treffer entfernen. 1:n-Joins können Kennzahlen vervielfachen.
Abfragen & Kennzahlen
Fehlende Beziehungen finden
Kunden ohne Bestellung finden, ohne NULL-Falle von NOT IN.
Beispiel kopieren
SELECT c.customer_id
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);
Schlüsselbeziehung und gewünschte Geschäftsbedeutung zuerst klären.
Abfragen & Kennzahlen
Zwischenschritt benennen
Eine Aggregation verständlich in zwei Leseschritte zerlegen.
Beispiel kopieren
WITH totals AS (
SELECT customer_id, SUM(amount) AS total
FROM orders GROUP BY customer_id
)
SELECT customer_id, total FROM totals WHERE total > 1000;
Eine CTE garantiert keine Materialisierung und keine Performanceverbesserung.
Datenqualität
Doppelte Schlüssel finden
Vor einem Join die Eindeutigkeit der Schlüsselseite prüfen.
Beispiel kopieren
SELECT order_id, COUNT(*) AS occurrences
FROM orders
GROUP BY order_id
HAVING COUNT(*) > 1;
Keine Ergebniszeile bedeutet nur: keine Dublette unter diesen Filtern. NULL-Schlüssel separat prüfen.
Abfragen & Kennzahlen
Neueste Version ohne QUALIFY
Eine stabile Zeile je Kunde auswählen.
Beispiel kopieren
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY updated_at DESC, record_id DESC
) AS rn FROM customer_versions
)
SELECT customer_id, updated_at FROM ranked WHERE rn = 1;
record_id löst Zeitgleichheit auf. NULL-Zeitstempel vorab bewusst behandeln.
Abfragen & Kennzahlen
Laufende Summe
Kumulierten Wert berechnen, ohne Detailzeilen zu verlieren.
Beispiel kopieren
SELECT order_id, order_date, amount,
SUM(amount) OVER (
ORDER BY order_date, order_id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_amount
FROM orders;
Explizites ROWS-Fenster und eindeutige Sortierung vermeiden überraschende Gruppierung bei gleichem Datum.
Datenqualität
Division durch null vermeiden
Leere Ergebnismengen und numerische Division unterscheiden.
Beispiel kopieren
SELECT SUM(amount) / NULLIF(COUNT(amount), 0)::numeric AS mean_amount
FROM orders;
PostgreSQL-Cast; Syntax anderer Datenbanken anpassen. NULL ist nicht automatisch fachlich null Euro. COUNT(amount) schließt fehlende Beträge aus.
Abfragen & Kennzahlen
Bedingt aggregieren
Mehrere fachliche Teilmengen in derselben Gruppierung auswerten.
Beispiel kopieren
SELECT customer_id,
SUM(CASE WHEN status = 'OPEN' THEN amount ELSE 0 END) AS open_amount
FROM orders GROUP BY customer_id;
CASE ersetzt keine Zeilenrechte. Summen vor und nach Joins auf Vervielfachung prüfen.
Datenqualität
Verwaiste Fremdschlüssel prüfen
Bestellungen ohne zugehörigen Kunden finden.
Beispiel kopieren
SELECT o.order_id, o.customer_id
FROM orders o
WHERE o.customer_id IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM customers c
WHERE c.customer_id = o.customer_id);
NULL-Fremdschlüssel werden hier absichtlich ausgeschlossen und benötigen eine eigene Pflichtfeldprüfung.
Modellierung
UNION und UNION ALL unterscheiden
Gleich strukturierte Bestände zusammenführen.
Beispiel kopieren
SELECT order_id, amount FROM orders_current
UNION ALL
SELECT order_id, amount FROM orders_archive;
UNION ALL erhält Dubletten. UNION entfernt gleiche Gesamtzeilen, nicht automatisch doppelte fachliche Schlüssel.
Modellierung
Join-Kardinalität sichtbar machen
Vor Summierung den Wechsel von Bestellung zu Bestellposition erkennen.
Beispiel kopieren
SELECT o.order_id, COUNT(*) AS joined_rows
FROM orders o JOIN order_lines l ON l.order_id = o.order_id
GROUP BY o.order_id
ORDER BY o.order_id;
Ein Kopfwert wird je Position wiederholt. Voraggregieren oder den fachlich passenden Grain wählen.
Betrieb & Fehlersuche
Änderung im Test zurückrollen
Eine DML-Änderung ausschließlich im freigegebenen Testkontext prüfen.
Beispiel kopieren
BEGIN;
UPDATE orders SET status = 'REVIEW' WHERE order_id = 1001;
SELECT order_id, status FROM orders WHERE order_id = 1001;
ROLLBACK;
ROLLBACK wirkt auf diese Transaktion; externe Effekte und datenbankspezifisches DDL sind gesondert zu bewerten.
Abfragen & Kennzahlen
Vorherigen Wert vergleichen
Veränderungen entlang einer festen Reihenfolge sichtbar machen.
Beispiel kopieren
SELECT order_id, amount,
LAG(amount) OVER (ORDER BY order_date, order_id) AS previous_amount
FROM orders;
Erste Zeile hat keinen Vorgänger; bei mehreren Kunden PARTITION BY ergänzen.
Abfragen & Kennzahlen
Teilmenge mit FILTER zählen
PostgreSQL-Aggregate für unterschiedliche Teilmengen bilden.
Beispiel kopieren
SELECT COUNT(*) AS all_orders,
COUNT(*) FILTER (WHERE status = 'OPEN') AS open_orders
FROM orders;
FILTER ist hier Aggregatsyntax; SQL-Dialekt prüfen.
Abfragen & Kennzahlen
Zeitraum ohne Endtagslücke
Alle Oktober-Zeitpunkte einschließlich Sekundenbruchteilen erfassen.
Beispiel kopieren
SELECT order_id, ordered_at FROM orders
WHERE ordered_at >= TIMESTAMP '2026-10-01 00:00:00'
AND ordered_at < TIMESTAMP '2026-11-01 00:00:00';
Für Zeitstempel mit Zeitzone die fachliche Zeitzone ausdrücklich festlegen.
Betrieb & Fehlersuche
Plan ohne ANALYZE lesen
Eine Abfrage vor Ausführung grob untersuchen.
Beispiel kopieren
EXPLAIN
SELECT order_id FROM orders WHERE customer_id = 42;
EXPLAIN ANALYZE führt die Anweisung tatsächlich aus; das ist ein anderer Prüfschritt.
Datenqualität
Leere Texte getrennt prüfen
NULL und leere oder nur aus Leerzeichen bestehende Texte finden.
Beispiel kopieren
SELECT customer_id, reference FROM customers
WHERE reference IS NULL OR TRIM(reference) = '';
Welche Zeichen als Leerraum gelten, hängt von Funktion und Daten ab; Rohwert bei Bereinigung nachvollziehbar halten.
Abfragen & Kennzahlen
Drei höchste Bestellungen je Kunde
Mit ROW_NUMBER eine feste Höchstzahl pro Gruppe auswählen.
Beispiel kopieren
WITH ranked AS (
SELECT order_id, customer_id, amount,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY amount DESC NULLS LAST, order_id
) AS position
FROM orders
)
SELECT order_id, customer_id, amount
FROM ranked WHERE position <= 3
ORDER BY customer_id, position;
NULLS LAST ist hier PostgreSQL-Syntax. order_id muss eindeutig sein. Bei Gleichständen werden höchstens drei Zeilen geliefert; für geteilte Plätze RANK oder DENSE_RANK prüfen.
Abfragen & Kennzahlen
Ränge ohne Lücken
Gleiche Beträge erhalten denselben Rang innerhalb eines Kunden.
Beispiel kopieren
SELECT order_id, customer_id, amount,
DENSE_RANK() OVER (
PARTITION BY customer_id ORDER BY amount DESC
) AS amount_rank
FROM orders
WHERE amount IS NOT NULL
ORDER BY customer_id, amount_rank, order_id;
Keinen eindeutigen Schlüssel in die Fenster-Sortierung aufnehmen, wenn gleiche Beträge gleichrangig bleiben sollen. Die äußere Sortierung bestimmt nur die Anzeige.
Abfragen & Kennzahlen
Letzten Wert der gesamten Gruppe lesen
LAST_VALUE mit vollständigem Fenster statt nur bis zur aktuellen Zeile nutzen.
Beispiel kopieren
SELECT order_id, customer_id,
LAST_VALUE(amount) OVER (
PARTITION BY customer_id
ORDER BY order_date, order_id
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING
) AS final_amount
FROM orders
ORDER BY customer_id, order_date, order_id;
Das Standardfenster endet bei ORDER BY häufig bei der aktuellen Peer-Gruppe. Fehlende Datums- und Betragswerte vorher fachlich behandeln.
Abfragen & Kennzahlen
Anteil am Gesamtbetrag
Detailzeilen mit ihrem Anteil an der Summe verbinden.
Beispiel kopieren
SELECT order_id, amount,
1.0 * amount / NULLIF(SUM(amount) OVER (), 0) AS share
FROM orders
WHERE amount IS NOT NULL
ORDER BY order_id;
Das Ergebnis ist ein Anteil, keine Prozentzahl. Das WHERE bestimmt die Grundgesamtheit. Negative Beträge können negative oder über 1 liegende Anteile erzeugen.
Abfragen & Kennzahlen
Vorhandene Beziehungen mit EXISTS prüfen
Kunden mit mindestens einer offenen Bestellung ohne Vervielfachung lesen.
Beispiel kopieren
SELECT c.customer_id
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
AND o.status = 'OPEN'
)
ORDER BY c.customer_id;
EXISTS liefert jede Ausgangszeile höchstens einmal, entfernt aber keine Dubletten, die bereits in customers vorhanden sind.
Datenqualität
Änderungen einschließlich NULL erkennen
Zwei Werte vergleichen, ohne Änderungen von oder nach NULL zu verlieren.
Beispiel kopieren
SELECT customer_id, previous_email, current_email
FROM customer_changes
WHERE previous_email IS DISTINCT FROM current_email
ORDER BY customer_id;
PostgreSQL behandelt bei IS DISTINCT FROM NULL wie einen vergleichbaren Wert. Beide NULL gelten als unverändert. Unterstützung im Zieldialekt prüfen.
Datenqualität
Zusammengesetzte Schlüssel prüfen
Dubletten auf dem tatsächlichen Grain einer Bestellposition finden.
Beispiel kopieren
SELECT order_id, line_id, COUNT(*) AS occurrences
FROM order_lines
GROUP BY order_id, line_id
HAVING COUNT(*) > 1
ORDER BY order_id, line_id;
Schlüsselspalten nicht als Zeichenketten verketten: Trennzeichen und NULL können Kollisionen verursachen. Pflichtfelder separat prüfen.
Datenqualität
Fehlende Werte und Quote zählen
COUNT(*) und COUNT(spalte) zur Vollständigkeitsprüfung kombinieren.
Beispiel kopieren
SELECT COUNT(*) AS total_rows,
COUNT(*) - COUNT(amount) AS missing_amounts,
1.0 * (COUNT(*) - COUNT(amount))
/ NULLIF(COUNT(*), 0) AS missing_rate
FROM orders;
Leere Tabelle: total_rows ist 0, missing_rate ist NULL. Ein Betrag von 0 gilt als vorhanden; leere Texte brauchen eine andere Regel.
Abfragen & Kennzahlen
Gewichteten Durchschnitt bilden
Mengen berücksichtigen, statt Zeilendurchschnitte ungewichtet zu mitteln.
Beispiel kopieren
SELECT 1.0 * SUM(unit_price * quantity)
/ NULLIF(SUM(quantity), 0) AS weighted_unit_price
FROM order_items
WHERE unit_price IS NOT NULL AND quantity > 0;
Diese Regel schließt Retouren, fehlende Preise und nicht positive Mengen bewusst aus. Gewichtung und Ausschlüsse fachlich bestätigen; Datentyp und Überlauf prüfen.
Modellierung
Kinddaten vor dem Join aggregieren
Eine Zeile pro Bestellung herstellen, bevor Kopf- und Positionsdaten verbunden werden.
Beispiel kopieren
WITH line_totals AS (
SELECT order_id, SUM(line_amount) AS line_amount
FROM order_items
GROUP BY order_id
)
SELECT o.order_id, o.amount AS header_amount,
l.line_amount
FROM orders o
LEFT JOIN line_totals l ON l.order_id = o.order_id
ORDER BY o.order_id;
orders muss bereits eine Zeile je order_id haben. NULL bei fehlenden Positionen bewusst beibehalten; nicht ohne fachliche Regel zu 0 machen.
Abfragen & Kennzahlen
Monate sauber zusammenfassen
Mit DATE_TRUNC einen Monatsbeginn statt eines formatierten Textes gruppieren.
Beispiel kopieren
SELECT DATE_TRUNC('month', ordered_at) AS month_start,
SUM(amount) AS total_amount
FROM orders
WHERE ordered_at IS NOT NULL
GROUP BY DATE_TRUNC('month', ordered_at)
ORDER BY month_start;
PostgreSQL-Syntax. Bei timestamptz hängt die Monatsgrenze von der Sitzungszeitzone ab; gewünschte Geschäftszeitzone ausdrücklich festlegen. Monate ohne Daten fehlen.
Datenqualität
Fehlende Kalendertage finden
Eine Datumsreihe gegen vorhandene Buchungstage prüfen.
Beispiel kopieren
WITH days AS (
SELECT DATE '2026-10-01' + n AS day
FROM generate_series(0, 30) AS series(n)
), actual AS (
SELECT DISTINCT order_date AS day FROM orders
)
SELECT d.day
FROM days d
LEFT JOIN actual a ON a.day = d.day
WHERE a.day IS NULL
ORDER BY d.day;
PostgreSQL: date + integer und generate_series. order_date wird hier als DATE vorausgesetzt. Ein fehlender Buchungstag ist nicht automatisch ein Datenfehler.
Betrieb & Fehlersuche
Stabil weiterblättern ohne OFFSET
Nach dem letzten eindeutigen Schlüssel der vorherigen Seite weiterlesen.
Beispiel kopieren
SELECT order_id, customer_id, amount
FROM orders
WHERE order_id > 1000
ORDER BY order_id
FETCH FIRST 50 ROWS ONLY;
1000 ist der letzte gelesene Schlüssel und muss als gebundener Parameter übergeben werden. order_id muss eindeutig und nicht NULL sein. Gleichzeitige Änderungen benötigen eine passende Konsistenzstrategie.
Abfragen & Kennzahlen
Detailgruppen und Gesamtsumme kombinieren
GROUPING SETS für Kundenwerte und eine zusätzliche Gesamtzeile nutzen.
Beispiel kopieren
SELECT customer_id,
GROUPING(customer_id) AS is_grand_total,
SUM(amount) AS total_amount
FROM orders
GROUP BY GROUPING SETS ((customer_id), ())
ORDER BY is_grand_total, customer_id;
PostgreSQL unterstützt GROUPING SETS. GROUPING unterscheidet die Gesamtzeile von einer echten NULL-Kundengruppe. Ausgabe beider Ebenen nicht nochmals ungefiltert summieren.
Abfragen & Kennzahlen
Textwerte geordnet zusammenfassen
Eine reproduzierbare Liste je Kunde statt zufälliger Aggregatreihenfolge erzeugen.
Beispiel kopieren
SELECT customer_id,
STRING_AGG(order_id::text, ', ' ORDER BY order_id)
AS order_ids
FROM orders
GROUP BY customer_id
ORDER BY customer_id;
PostgreSQL-Syntax einschließlich Cast und ORDER BY im Aggregat. Dubletten bleiben enthalten. Für große Gruppen oder sensible Daten keine unbegrenzt langen Listen ausgeben.
Abfragen & Kennzahlen
JSON-Textwert gezielt lesen
Ein einzelnes Attribut aus einer JSONB-Spalte extrahieren.
Beispiel kopieren
SELECT event_id,
payload ->> 'customer_id' AS customer_id_text
FROM events
WHERE payload ->> 'event_type' = 'order_created'
ORDER BY event_id;
PostgreSQL: ->> liefert Text. Fehlender Schlüssel oder JSON-null ergibt SQL-NULL; Datentyp und Schema vor einer Typumwandlung prüfen.
Abfragen & Kennzahlen
Median statt Durchschnitt berechnen
Eine robuste Lagekennzahl mit PERCENTILE_CONT bestimmen.
Beispiel kopieren
SELECT PERCENTILE_CONT(0.5)
WITHIN GROUP (ORDER BY amount) AS median_amount
FROM orders
WHERE amount IS NOT NULL;
PostgreSQL unterstützt ordered-set aggregates. PERCENTILE_CONT interpoliert; der Median muss kein vorhandener Einzelwert sein. Bei leerem Bestand ist das Ergebnis NULL.
Modellierung
Hierarchie mit Tiefengrenze lesen
Eine Organisationsstruktur rekursiv ab einem Startknoten durchlaufen.
Beispiel kopieren
WITH RECURSIVE tree AS (
SELECT id, parent_id, 0 AS depth
FROM departments WHERE id = 1
UNION ALL
SELECT d.id, d.parent_id, t.depth + 1
FROM departments d
JOIN tree t ON d.parent_id = t.id
WHERE t.depth < 10
)
SELECT id, parent_id, depth FROM tree
ORDER BY depth, id;
1 ist ein Beispiel für den Startknoten. Die Tiefengrenze verhindert unendliche Rekursion, erkennt aber keine Zyklen und kann echte Äste abschneiden. Für Produktion Zyklusprüfung und passende Grenze ergänzen.
Datenqualität
Bestände mit EXCEPT abgleichen
Schlüssel aus dem erwarteten Bestand finden, die im tatsächlichen Bestand fehlen.
Beispiel kopieren
SELECT customer_id FROM expected_customers
EXCEPT
SELECT customer_id FROM customers;
EXCEPT entfernt Dubletten und prüft Mengen, keine Häufigkeiten. Spaltenzahl und Datentypen müssen kompatibel sein. Für den umgekehrten Abgleich die Seiten tauschen.
Datenqualität
Wertebereich inklusive NULL prüfen
Fehlende und unplausible Mengen getrennt benennen.
Beispiel kopieren
SELECT order_id, quantity,
CASE
WHEN quantity IS NULL THEN 'MISSING'
WHEN quantity < 0 THEN 'NEGATIVE'
WHEN quantity > 10000 THEN 'ABOVE_LIMIT'
END AS issue
FROM order_items
WHERE quantity IS NULL OR quantity < 0 OR quantity > 10000
ORDER BY order_id;
Grenzen sind Beispiele. Retouren oder große Sammelaufträge können gültig sein; fachliche Regel vor dem Einsatz abstimmen.
Tests & Auswahl
Testdaten direkt in der Abfrage
Kleine Grenzfälle ohne neue Tabelle nachvollziehen.
Beispiel kopieren
WITH sample(id, amount) AS (
VALUES (1, 10), (2, 0), (3, NULL)
)
SELECT id, amount FROM sample ORDER BY id;
Datentypen bei gemischten Werten ausdrücklich festlegen. VALUES ist für kleine Testmengen gedacht.
Abfragen & Kennzahlen
NULL beim Sortieren bewusst platzieren
Fehlende Beträge unabhängig von der Sortierrichtung ans Ende setzen.
Beispiel kopieren
SELECT order_id, amount FROM orders
ORDER BY amount DESC NULLS LAST, order_id;
PostgreSQL unterstützt NULLS LAST. Für andere Dialekte alternativ nach CASE WHEN amount IS NULL THEN 1 ELSE 0 END sortieren.
Abfragen & Kennzahlen
Nächste Bestellung je Kunde finden
Mit LEAD die nächste Zeile in einer stabilen Reihenfolge lesen.
Beispiel kopieren
SELECT order_id, customer_id,
LEAD(order_id) OVER (
PARTITION BY customer_id ORDER BY order_date, order_id
) AS next_order_id
FROM orders
ORDER BY customer_id, order_date, order_id;
Die letzte Zeile je Kunde liefert NULL. order_id löst gleiche Datumswerte auf; fehlende Zeitangaben vorher klären.
Abfragen & Kennzahlen
Gleitenden Mittelwert über drei Zeilen
Die aktuelle und die zwei vorherigen Zeilen gemeinsam auswerten.
Beispiel kopieren
SELECT order_id, customer_id, amount,
AVG(amount) OVER (
PARTITION BY customer_id ORDER BY order_date, order_id
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_average
FROM orders
ORDER BY customer_id, order_date, order_id;
Drei Zeilen sind nicht drei Kalendertage. Am Gruppenanfang sind es weniger Zeilen; AVG ignoriert NULL.
Abfragen & Kennzahlen
Veränderungsrate zum Vorgänger
Den vorherigen Betrag pro Kunde als Bezugsgröße nutzen.
Beispiel kopieren
WITH previous_values AS (
SELECT order_id, customer_id, amount,
LAG(amount) OVER (
PARTITION BY customer_id ORDER BY order_date, order_id
) AS previous_amount
FROM orders
)
SELECT order_id, customer_id,
1.0 * (amount - previous_amount)
/ NULLIF(previous_amount, 0) AS change_rate
FROM previous_values ORDER BY customer_id, order_id;
Erste Zeile und Vorgängerbetrag 0 liefern NULL. Negative Bezugsbeträge benötigen eine fachliche Deutung. Für Prozentanzeige mit 100 multiplizieren.
Abfragen & Kennzahlen
Gruppengröße neben Details zeigen
Bestellungen zählen, ohne einzelne Zeilen zu verdichten.
Beispiel kopieren
SELECT order_id, customer_id,
COUNT(*) OVER (PARTITION BY customer_id) AS customer_orders
FROM orders ORDER BY customer_id, order_id;
Ohne ORDER BY im Fenster zählt COUNT die gesamte Partition. NULL-Kunden bilden eine eigene Gruppe.
Datenqualität
Zwei Bestände vollständig abstimmen
Fehlende und abweichende Beträge beider Seiten erkennen.
Beispiel kopieren
SELECT COALESCE(a.order_id, b.order_id) AS order_id,
a.amount AS source_amount, b.amount AS target_amount
FROM source_orders a
FULL OUTER JOIN target_orders b ON b.order_id = a.order_id
WHERE a.order_id IS NULL OR b.order_id IS NULL
OR a.amount IS DISTINCT FROM b.amount
ORDER BY order_id;
Beide Schlüssel müssen eindeutig und nicht NULL sein. PostgreSQL-Syntax; Rundung, Währung und Vergleichstoleranzen gesondert festlegen.
Abfragen & Kennzahlen
Letzte Bestellung pro Kunde mit LATERAL
Je Kunde höchstens einen passenden Detaildatensatz ergänzen.
Beispiel kopieren
SELECT c.customer_id, latest.order_id, latest.order_date
FROM customers c
LEFT JOIN LATERAL (
SELECT o.order_id, o.order_date FROM orders o
WHERE o.customer_id = c.customer_id
ORDER BY o.order_date DESC NULLS LAST, o.order_id DESC
FETCH FIRST 1 ROW ONLY
) latest ON TRUE
ORDER BY c.customer_id;
PostgreSQL-Beispiel. Kunden ohne Bestellung bleiben erhalten. Einen passenden Index und den Ausführungsplan prüfen.
Abfragen & Kennzahlen
JSON-Array mit Position auflösen
Array-Elemente einer JSONB-Spalte in einzelne Zeilen überführen.
Beispiel kopieren
SELECT e.event_id, item.position, item.value
FROM events e
CROSS JOIN LATERAL jsonb_array_elements(e.payload -> 'items')
WITH ORDINALITY AS item(value, position)
ORDER BY e.event_id, item.position;
PostgreSQL: items muss ein Array sein. Leere oder fehlende Arrays erzeugen hier keine Zeilen; Objekte oder JSON-null vorher validieren. Position beginnt bei 1.
Modellierung
Breite Messwertspalten entpivotieren
Mehrere Kennzahlspalten als Name-Wert-Zeilen ausgeben.
Beispiel kopieren
SELECT m.customer_id, v.metric, v.value
FROM customer_metrics m
CROSS JOIN LATERAL (
VALUES ('revenue', m.revenue), ('cost', m.cost)
) v(metric, value)
ORDER BY m.customer_id, v.metric;
PostgreSQL-Beispiel. Die Wertespalten müssen kompatible Datentypen haben. NULL-Werte bleiben erhalten; Einheiten nicht vermischen.
Datenqualität
Alle Zeilen einer Regel prüfen
BOOL_AND mit expliziter Behandlung fehlender Werte kombinieren.
Beispiel kopieren
SELECT COUNT(*) AS checked_rows,
BOOL_AND(COALESCE(amount >= 0, FALSE)) AS all_nonnegative
FROM orders;
PostgreSQL. NULL-Beträge gelten hier als Verstoß. Bei leerer Tabelle liefert BOOL_AND NULL; checked_rows muss für die Abnahme größer als 0 sein.
Modellierung
Historische Dimension zeitgerecht zuordnen
Ein Faktum an die zur Buchungszeit gültige Dimensionsversion binden.
Beispiel kopieren
SELECT o.order_id, d.customer_key
FROM orders o
LEFT JOIN dim_customer_history d
ON d.customer_id = o.customer_id
AND o.ordered_at >= d.valid_from
AND (o.ordered_at < d.valid_to OR d.valid_to IS NULL)
ORDER BY o.order_id;
Intervalle sind links geschlossen und rechts offen. Überlappende Versionen vervielfachen Fakten; Zeittypen, Zeitzonen und Eindeutigkeit vorab prüfen.
Abfragen & Kennzahlen
Eine Zeile je Schlüssel mit DISTINCT ON
Die neueste Version mit einer PostgreSQL-Kurzform lesen.
Beispiel kopieren
SELECT DISTINCT ON (customer_id)
customer_id, record_id, updated_at
FROM customer_versions
ORDER BY customer_id, updated_at DESC NULLS LAST, record_id DESC;
Nur PostgreSQL. DISTINCT ON muss zu den linken ORDER-BY-Ausdrücken passen. Eindeutigen Tie-Breaker verwenden; für portablere Abfragen ROW_NUMBER nutzen.
Datenqualität
Zusätzliche Schlüsselzeilen quantifizieren
Die Anzahl zusätzlicher Zeilen neben eindeutigen Schlüsseln messen.
Beispiel kopieren
SELECT COUNT(order_id) AS populated_keys,
COUNT(order_id) - COUNT(DISTINCT order_id) AS extra_key_rows
FROM orders;
NULL-Schlüssel werden hier nicht gezählt und brauchen eine eigene Regel. Drei gleiche Schlüssel bedeuten zwei zusätzliche Zeilen, nicht drei.
Datenqualität
Schlüssel kontrolliert normalisieren
Leerzeichen und Großschreibung für einen vorgeschlagenen Vergleichsschlüssel vereinheitlichen.
Beispiel kopieren
SELECT customer_id, external_code,
NULLIF(LOWER(TRIM(external_code)), '') AS normalized_code
FROM customers ORDER BY customer_id;
Nur verwenden, wenn Großschreibung fachlich irrelevant ist. Neue Kollisionen prüfen; Originalwert erhalten. TRIM entfernt nicht automatisch jede Art Unicode-Leerraum.
Abfragen & Kennzahlen
Unterstrich im Suchmuster wörtlich behandeln
Nach dem Präfix AB_ suchen statt nach einem beliebigen Zeichen.
Beispiel kopieren
SELECT product_id, product_code
FROM products
WHERE product_code LIKE 'AB!_%' ESCAPE '!'
ORDER BY product_id;
! maskiert hier den Unterstrich, % bleibt Platzhalter. Bei dynamischen Suchwerten Parameterbindung und Muster-Escaping getrennt behandeln.
Datenqualität
Überschneidende Zeiträume finden
Zwei abgeschlossene, nicht leere Zeitintervalle paarweise prüfen.
Beispiel kopieren
SELECT a.period_id AS first_id, b.period_id AS second_id
FROM periods a
JOIN periods b ON a.entity_id = b.entity_id
AND a.period_id < b.period_id
AND a.start_at < b.end_at
AND b.start_at < a.end_at
ORDER BY a.period_id, b.period_id;
Voraussetzung: start_at < end_at, beide Grenzen nicht NULL. Halboffene Intervalle: Berührung am Endpunkt ist keine Überschneidung. Offene Enden benötigen eine ergänzende Regel.
Datenqualität
Mehrere Pflichtfelder gemeinsam prüfen
Zeilen mit fehlendem Kunden, Datum oder Betrag finden.
Beispiel kopieren
SELECT order_id, customer_id, ordered_at, amount
FROM orders
WHERE customer_id IS NULL OR ordered_at IS NULL OR amount IS NULL
ORDER BY order_id;
OR findet jeden einzelnen Verstoß; AND würde nur Zeilen finden, bei denen alle drei Werte fehlen. 0 ist kein NULL.
Betrieb & Fehlersuche
Teilschritt im Test zurückrollen
Einen Savepoint innerhalb einer anschließend vollständig zurückgerollten Transaktion nutzen.
Beispiel kopieren
BEGIN;
SAVEPOINT before_change;
UPDATE orders SET status = 'REVIEW' WHERE order_id = 1001;
ROLLBACK TO SAVEPOINT before_change;
SELECT order_id, status FROM orders WHERE order_id = 1001;
ROLLBACK;
PostgreSQL-Beispiel für eine freigegebene Testumgebung. ROLLBACK TO beendet die Transaktion nicht. Trigger mit externen Effekten gesondert beachten.
Sicherheit & Rechte
Werte als Parameter übergeben
PostgreSQL PREPARE ohne Zusammenbauen einer SQL-Zeichenkette verwenden.
Beispiel kopieren
PREPARE orders_by_status(text) AS
SELECT order_id, amount FROM orders
WHERE status = $1 ORDER BY order_id;
EXECUTE orders_by_status('OPEN');
DEALLOCATE orders_by_status;
PREPARE ist sitzungsgebunden. Anwendungscode sollte die Parameter-API des Treibers nutzen. Tabellen- und Spaltennamen können nicht als Werteparameter gebunden werden.