Daten im SQL Server validieren

Ein praktischer Leitfaden zur Datenvalidierung in Microsoft SQL Server — die sechs Prüfungen, die jede Tabelle braucht, das T-SQL dazu, und wie daraus ein versionierter Data Contract wird, der nach Zeitplan läuft.

· 8 min read

Der schnellste Weg, Daten im SQL Server zu validieren, besteht darin, jede Erwartung als einzelne Aggregatabfrage zu formulieren, die eine Anzahl von Verstößen zurückgibt, alle Abfragen in einem Durchgang gegen die Tabelle laufen zu lassen und den Lauf fehlschlagen zu lassen, sobald eine Anzahl ihren Schwellenwert überschreitet. Sechs Prüfungen decken die meisten realen Ausfälle ab: NULL-Werte in Pflichtspalten, doppelte Schlüssel, Werte außerhalb einer erlaubten Menge, Zahlen außerhalb eines plausiblen Bereichs, fehlerhaft formatierte Zeichenketten und veraltete Zeilen. Dieser Leitfaden zeigt das T-SQL für jede davon, die Eigenheiten des SQL Server, die Ihnen zum Verhängnis werden, und den Weg von Ad-hoc-Skripten zu einem Contract, der nach Zeitplan läuft.

Warum SQL Server einen eigenen Ansatz braucht

Die meisten Ratschläge zur Datenqualität sind für Postgres geschrieben und setzen stillschweigend Dinge voraus, die T-SQL nicht hat. Drei Unterschiede zählen:

Alles Folgende ist mit diesen Punkten im Hinterkopf geschrieben. Für die Teile, die auf jeder Engine gleich sind — wo Prüfungen laufen sollten, wie man sie plant, welche Tabellen Vorrang haben —, beginnen Sie mit dem vollständigen Leitfaden zur Datenvalidierung.

Die sechs Prüfungen, die jede Tabelle braucht

1. Vollständigkeit — NULL-Werte in Pflichtspalten

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [order_id] IS NULL;

Verwenden Sie auf großen Faktentabellen COUNT_BIG statt COUNT: COUNT liefert einen int und läuft jenseits von 2,1 Milliarden Zeilen über. Beachten Sie außerdem, dass COUNT(column) NULL-Werte bereits überspringt — deshalb ist die Prüfung als gefiltertes COUNT(*) geschrieben. Die beiden sind leicht zu verwechseln, und die falsche Variante meldet immer null Verstöße.

2. Eindeutigkeit — doppelte Geschäftsschlüssel

SELECT COUNT_BIG(*) AS violations
FROM (
    SELECT [order_id]
    FROM [sales].[orders]
    WHERE [order_id] IS NOT NULL
    GROUP BY [order_id]
    HAVING COUNT(*) > 1
) AS dupes;

Ein Primärschlüssel-Constraint würde das verhindern, aber Analysetabellen werden meist von einem ELT-Job in einen Heap ohne jedes Constraint geladen. Die Duplikatprüfung ist das, was ein fehlendes Constraint Sie kostet.

3. Konformität — Werte außerhalb einer erlaubten Menge

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [status] IS NOT NULL
  AND [status] NOT IN ('pending', 'paid', 'shipped', 'refunded');

Lassen Sie die Absicherung mit IS NOT NULL stehen. NOT IN mit einem NULL-Wert auf der linken Seite ergibt UNKNOWN, die Zeile fällt heraus, und eine Spalte, die zu 40 % NULL ist, wirkt vollkommen konform.

4. Genauigkeit — Zahlen außerhalb eines plausiblen Bereichs

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [total_amount] IS NOT NULL
  AND ([total_amount] < 0 OR [total_amount] > 100000);

Bereichsprüfungen fangen Einheitenfehler ab — Cent als Euro geladen, ein durch eine Währungsumrechnung verschobenes Komma —, die kein Schema-Constraint je zu sehen bekommt.

5. Konformität — fehlerhaft formatierte Bezeichner

Hier weicht T-SQL ab. Es gibt keinen ~-Operator, ein verankertes Muster muss also als LIKE ausgedrückt werden:

-- ^ORD-[0-9]{6}$  becomes:
SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [reference] IS NOT NULL
  AND [reference] NOT LIKE 'ORD-[0-9][0-9][0-9][0-9][0-9][0-9]';

LIKE ist an beiden Enden implizit verankert, wenn das Muster kein führendes oder abschließendes % enthält — das ist also ein echtes Äquivalent zur verankerten Regex. Nicht ausdrücken lassen sich Alternativen, optionale Gruppen, Rückverweise und unbegrenzte Wiederholungen. Dafür greifen Sie besser auf eine explizite Liste erlaubter Werte oder eine handgeschriebene customSql-Prüfung zurück, statt so zu tun, als wäre LIKE eine Regex-Engine.

Catalyst übernimmt diese Übersetzung für Sie: Eine regex-Regel auf einer SQL-Server-Verbindung wird in das entsprechende LIKE-Muster überführt, sofern sie sich ausdrücken lässt, und löst andernfalls einen expliziten Fehler „auf diesem Dialekt nicht unterstützt" aus — die Prüfung scheitert also lautstark, statt stillschweigend durchzulaufen.

6. Aktualität — Freshness

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [created_at] < DATEADD(HOUR, -24, SYSUTCDATETIME());

Verwenden Sie SYSUTCDATETIME(), nicht GETDATE(). GETDATE() liefert die lokale Zeit des Servers, dieselbe Prüfung verschiebt sich also zweimal im Jahr um eine Stunde und über Regionen hinweg um ganze Stunden. Speichern Sie Zeitstempel als datetime2 und vergleichen Sie in UTC.

Einen sicheren Nur-Lese-Login einrichten

Validierung sollte nie Schreibzugriff brauchen. Ein minimaler Login sieht so aus:

CREATE LOGIN catalyst_ro WITH PASSWORD = '<generated>';
CREATE USER catalyst_ro FOR LOGIN catalyst_ro;
ALTER ROLE db_datareader ADD MEMBER catalyst_ro;
GRANT VIEW DEFINITION TO catalyst_ro;   -- needed to read INFORMATION_SCHEMA

db_datareader gewährt SELECT auf jeder Tabelle; VIEW DEFINITION ist das, was dem Konto erlaubt, Spalten und Typen aus INFORMATION_SCHEMA.COLUMNS aufzulisten. Richten Sie den Login auf ein lesbares sekundäres Replikat, wenn Sie eine Availability Group betreiben — Validierungsabfragen sind Aggregat-Scans und gehören von der Primärinstanz herunter.

Von Ad-hoc-SQL zum Data Contract

Skripte wie die obigen verrotten. Sie liegen in irgendjemandes Repository, niemand weiß, welche davon noch laufen, und die Schwellenwerte sind für die Analystinnen und Analysten, die darauf angewiesen sind, unsichtbar. Die Lösung besteht darin, die Erwartungen deklarativ zu formulieren, in einem Format, das Menschen wie Werkzeuge lesen können.

Catalyst nutzt dafür den Open Data Contract Standard (ODCS). Aus den sechs Prüfungen von oben wird:

apiVersion: v3.0.0
kind: DataContract
info:
  title: orders
  version: 1.2.0
  owner: data-platform
schema:
  - name: orders
    physicalName: orders
    physicalType: table
    properties:
      - name: order_id
        logicalType: string
        physicalType: uniqueidentifier
        required: true
        primaryKey: true
        quality:
          - rule: nullCount
            dimension: completeness
            severity: error
            mustBe: "0"
          - rule: duplicateCount
            dimension: uniqueness
            severity: error
            mustBe: "0"
      - name: status
        logicalType: string
        quality:
          - rule: validValues
            dimension: conformity
            severity: error
            mustBe: "['pending', 'paid', 'shipped', 'refunded']"
      - name: total_amount
        logicalType: number
        quality:
          - rule: between
            dimension: accuracy
            severity: error
            mustBe: "[0, 100000]"
      - name: reference
        logicalType: string
        quality:
          - rule: regex
            dimension: conformity
            severity: warning
            mustBe: "'^ORD-[0-9]{6}
#39;" - name: created_at logicalType: timestamp quality: - rule: freshness dimension: timeliness severity: error mustBe: "<= 24h"

Das YAML ist die Quelle der Wahrheit. Catalyst kompiliert jeden quality-Eintrag in das dialektkorrekte T-SQL von oben, führt den Lauf gegen Ihren Server aus und protokolliert pro Prüfung pass/warn/fail. Wird eine Regel im visuellen Builder bearbeitet, schreibt Catalyst dasselbe YAML zurück, Kommentare und Reihenfolge intakt — der im Pull Request geprüfte Contract ist also der Contract, der läuft.

Beachten Sie das Feld severity. error lässt den Lauf scheitern; warning protokolliert den Verstoß und macht weiter. Musterprüfungen auf manuell erfassten Feldern sind üblicherweise Warnungen — Sie wollen den Trend, nicht einen Pager-Alarm um 3 Uhr nachts.

Zeitplanung und Alarmierung

Eine Validierung, die Sie von Hand starten, ist eine Validierung, die Sie einmal starten. Richten Sie einen Zeitplan auf das Dataset — stündlich für Freshness auf einer Streaming-Tabelle, täglich nach dem ELT-Fenster für alles andere — und alarmieren Sie beim Übergang, nicht beim Zustand. Wichtig ist „Orders hat angefangen zu scheitern", nicht „Orders scheitert immer noch"; Letzteres ist genau das, was Menschen beibringt, den Channel zu ignorieren.

Zwei betriebliche Hinweise speziell für SQL Server:

SQL-Server-Stolperfallen, die Sie kennen sollten

FalleWas passiertWas zu tun ist
Überlauf bei COUNT(*)Scheitert jenseits von 2,1 Mrd. ZeilenCOUNT_BIG(*) verwenden
NOT IN mit NULL-WertenZeilen werden stillschweigend ausgeschlossenIS NOT NULL ergänzen
GETDATE()Lokale ServerzeitSYSUTCDATETIME() verwenden
Collation ohne Groß-/Kleinschreibung'PAID' besteht eine Prüfung auf 'paid'Collation festlegen oder mit COLLATE normalisieren
Implizite KonvertierungWHERE varchar_col = 123 scannt die ganze TabelleGleiche Typen vergleichen
Gleichheit bei floatBereichsgrenzen durch Rundung verfälschtFür Geldbeträge decimal verwenden

Häufig gestellte Fragen

Kann ich Daten im SQL Server validieren, ohne SQL zu schreiben?

Ja. Definieren Sie die Erwartung deklarativ — Pflichtfeld, eindeutig, erlaubte Werte, Zahlenbereich, Muster, maximales Alter — und lassen Sie das Werkzeug sie nach T-SQL kompilieren. Catalyst importiert die Spalten und Typen aus INFORMATION_SCHEMA, schlägt aus dem Schema einen Basissatz an Regeln vor und erzeugt die Abfragen. SQL schreiben Sie nur noch für wirklich maßgeschneiderte Logik, über eine customSql-Regel.

Braucht die Validierung der Datenqualität Schreibzugriff auf meine Datenbank?

Nein. Jede Prüfung in diesem Leitfaden ist ein SELECT, das eine einzige Zahl zurückgibt. Ein db_datareader-Login plus VIEW DEFINITION genügt. Catalyst verbindet sich ausschließlich lesend und speichert nur Metadaten und Prüfergebnisse — die Zeilen selbst bleiben auf Ihrem Server.

Wie prüfe ich reguläre Ausdrücke in T-SQL?

Direkt gar nicht — SQL Server hat keinen Regex-Operator. Verankerte Muster fester Länge lassen sich in LIKE mit Zeichenklassen im Stil von [0-9] übersetzen. Alles, was Alternativen, optionale Gruppen oder unbegrenzte Wiederholungen nutzt, muss zu einer Liste erlaubter Werte, einer CLR-Funktion oder einer eigenen SQL-Prüfung werden.

Wie oft sollten Validierungen laufen?

Richten Sie den Zeitplan nach dem Takt der Daten selbst und führen Sie die Prüfung direkt nach dem Ladevorgang aus, der sie erzeugt. Freshness-Regeln auf Streaming-Tabellen wollen stündliche Läufe; Batch-Dimensionstabellen wollen einen Lauf nach Abschluss des nächtlichen ELT. Häufiger zu prüfen, als sich die Daten ändern, erzeugt nur Rauschen.

Funktioniert das mit Azure SQL und Microsoft Fabric?

Azure SQL Database nutzt dieselbe T-SQL-Oberfläche, alles hier gilt also unverändert. Auch das Warehouse und der SQL-Analytics-Endpunkt von Microsoft Fabric sprechen T-SQL — mit demselben Fehlen von Regex. Die Unterschiede rund um die Entra-ID-Authentifizierung und die OneLake-Metadatensynchronisation finden Sie im Leitfaden zu Microsoft Fabric.