Datenqualität in SQL Server // TRY_CONVERT für decimal und numeric sicher anwenden

Wer einmal einen Preis-Import gesehen hat, der aus '123,45 €' ein NULL macht statt der erwarteten Dezimalzahl, weiß: TRY_CONVERT(decimal(5, 2), '123,45') liefert NULL, weil das Komma als Dezimaltrennzeichen nicht erlaubt ist. Und selbst wenn das Komma weg ist: TRY_CONVERT(decimal(5, 2), '1234.56') ist auch NULL — diesmal wegen einer Vorkommastelle zu viel.

Auf einen Blick:

  • decimal(p, s) und numeric(p, s) sind in SQL Server funktionell identisch — der Artikel verwendet decimal, alle Aussagen gelten 1:1 für numeric.
  • TRY_CONVERT rundet Nachkommastellen still auf s, lehnt aber überzählige Vorkommastellen mit NULL ab.
  • Bei der Text-nach-decimal-Konvertierung ergeben Komma als Dezimaltrennzeichen, leerer String und wissenschaftliche Notation jeweils NULL. Das Zwei-Stufen-Pattern (text → float → decimal) fängt die Notation ab — als Parsing-Workaround mit Präzisionsgrenze, nicht als Universalweg.
  • Postgres-Pendant ist numeric(p, s) oder decimal(p, s) (echte Synonyme). p darf bis 1000 gehen (vs. 38 in SQL Server) — ein TRY_CONVERT-Pendant fehlt dort allerdings, der Artikel zeigt den Wrapper.

Voraussetzung: TRY_CONVERT existiert seit SQL Server 2012. Das sichere Pattern nutzt TRIM und braucht daher SQL Server 2017+ (davor LTRIM(RTRIM(…))-Fallback). Die Postgres-Beispiele setzen keine bestimmte Version voraus.

Inhalt

Decimal und Numeric — funktionell identisch

SQL Server stellt für die Speicherung von exakten Dezimalzahlen zwei Datentypen zur Verfügung: decimal(p, s) und numeric(p, s). Beide sind Synonyme mit denselben Precision-/Scale-Regeln und demselben Konvertierungsverhalten. Sie sind funktionell identisch — eine Spalte vom Typ decimal(10, 2) und eine vom Typ numeric(10, 2) verhalten sich in Konvertierung und Arithmetik exakt gleich.

In diesem Artikel wird durchgehend decimal verwendet. Alle Aussagen gelten 1:1 für numeric.

Precision und Scale wählen

decimal(p, s) hat zwei Parameter:

  • precision (p) — die Gesamtzahl der Dezimalstellen, die der Datentyp speichern kann. Erlaubt sind Werte von 1 bis 38.
  • scale (s) — die Anzahl der Dezimalstellen rechts vom Dezimaltrennzeichen. Erlaubt sind Werte von 0 bis p.

Konkret: decimal(5, 2) speichert fünf Stellen insgesamt — drei vor und zwei nach dem Dezimaltrennzeichen. Damit ist sofort klar, warum 1234.56 scheitert und 123.456 gerundet wird.

Daraus ergibt sich der Wertebereich:

decimal(p, s)MinMaxTypische Verwendung
decimal(5, 2)−999,99+999,99Prozent-Sätze, kleine Preise
decimal(10, 2)−99 999 999,99+99 999 999,99Beträge im Buchhaltungs-Bereich
decimal(19, 4)−999 999 999 999 999,9999+999 999 999 999 999,9999money-ähnlich: gleiche Stellenzahl, anderer Wertebereich (vgl. TRY_CONVERT für money und smallmoney)
decimal(38, 10)(Max-Form, 28 Vorkomma- und 10 Nachkommastellen)(Max-Form, 28 Vorkomma- und 10 Nachkommastellen)finanzmathematische / wissenschaftliche Werte

Faustregel: Anzahl Vorkommastellen + Anzahl Nachkommastellen ≤ p. Wenn ein Wert mehr Vorkommastellen hat als p − s zulässt, ist die Konvertierung nicht möglich — TRY_CONVERT liefert dann NULL, ein CAST/CONVERT löst einen Fehler aus. Nachkommastellen werden hingegen still gerundet, ohne Fehler.

Rundung vs. Vorkomma-Overflow

Die Asymmetrie zwischen Nachkomma und Vorkomma ist die zentrale Falle beim Arbeiten mit decimal:

  1: SELECT TRY_CONVERT(decimal(5, 2), '123.456')   -- 123.46
  2: SELECT TRY_CONVERT(decimal(5, 2), '1234.56')   -- NULL
  3: SELECT TRY_CONVERT(decimal(5, 2), '12345.6')   -- NULL
  4: SELECT TRY_CONVERT(decimal(5, 2),  1234.56)    -- NULL
  5: SELECT TRY_CONVERT(decimal(5, 2),  123.456)    -- 123.46

  • Nachkomma wird auf s Stellen gerundet — kaufmännisch, die 5 rundet also vom Nullpunkt weg: 10.645 → 10.65-10.645 → -10.65 (half away from zero, kein Banker’s Rounding). Das passiert still, ohne Fehler, ohne NULL.
  • Vorkomma-Overflow liefert dagegen NULL — und zwar sowohl bei Text-Input (Zeilen 2, 3) als auch bei typisierten Zahlen (Zeile 4). Das NULL ist dabei TRY_CONVERT-spezifisch: Ein CAST/CONVERT löst an derselben Stelle einen Laufzeitfehler aus.

Kernaussage: Eine TRY_CONVERT(decimal(p, s), …)-Konvertierung kann den Eingabewert stillschweigend ändern (Nachkomma-Rundung) oder die Konvertierung stillschweigend mit NULL quittieren (Vorkomma-Overflow). In ETL-Pipelines ist letzteres oft die problematischere Falle: ein NULL im Ziel-Feld sieht aus wie „kein Wert geliefert“, war aber tatsächlich „Wert zu groß für die Skala“.

Text nach decimal konvertieren

Wird ein Wert vom Typ nvarchar/varchar an TRY_CONVERT übergeben, muss der Eingangswert eine Zahl darstellen. Als Dezimaltrennzeichen erwartet diese Text-nach-decimal-Konvertierung ausschließlich den Punkt. Tausendertrennzeichen sind nicht zulässig. Eine leere Zeichenfolge oder ein String nur aus Leerzeichen wird direkt zu NULL konvertiert — das ist ein wichtiger Unterschied zu float und money, wo leere Zeichenfolgen zu 0 konvertiert werden.

  1: SELECT TRY_CONVERT(decimal(5, 2), NULL        )  -- NULL
  2: SELECT TRY_CONVERT(decimal(5, 2), N'123'      )  -- 123.00
  3: SELECT TRY_CONVERT(decimal(5, 2), N'123,456'  )  -- NULL
  4: SELECT TRY_CONVERT(decimal(5, 2), N'123.456'  )  -- 123.46
  5: SELECT TRY_CONVERT(decimal(5, 2), N''         )  -- NULL
  6: SELECT TRY_CONVERT(decimal(5, 2), N' '        )  -- NULL
  7: SELECT TRY_CONVERT(decimal(5, 2), N' 123.456' )  -- 123.46
  8: SELECT TRY_CONVERT(decimal(5, 2), N'123.456 ' )  -- 123.46
  9: SELECT TRY_CONVERT(decimal(5, 2), N'1234.56'  )  -- NULL
 10: SELECT TRY_CONVERT(decimal(5, 2), N'123456E-3')  -- NULL

Die Zeilen zeigen im Einzelnen:

  • Zeile 3: '123,456' mit Komma ergibt NULL — das Komma wird nicht als Dezimaltrennzeichen erkannt. Anders als bei Datenqualität in SQL Server // TRY_CONVERT für money und smallmoney sicher anwenden, wo Kommas ignoriert werden.
  • Zeilen 5, 6: leere Zeichenfolge und ein String nur aus Leerzeichen werden zu NULL. Anders als bei floatrealmoney und smallmoney, wo das Ergebnis 0 wäre.
  • Zeilen 7, 8: führende und folgende Leerzeichen werden abgeschnitten und verhindern die Konvertierung nicht.
  • Zeile 9: '1234.56' hat zu viele Vorkommastellen für decimal(5, 2) (max p − s = 3 Vorkommastellen) → NULL.
  • Zeile 10: wissenschaftliche Notation '123456E-3' als Text ergibt NULL. Das ändert sich, wenn die Zahl als typisierter Wert übergeben wird (siehe nächste Sektion).

Typisierte Zahlen nach decimal konvertieren

Wird die Zahl bereits typisiert übergeben (intfloatdecimal), kann TRY_CONVERT jeden numerischen Wert in decimal konvertieren — solange die Vorkommastellen passen:

  1: SELECT TRY_CONVERT(decimal(5, 2), NULL     )  -- NULL
  2: SELECT TRY_CONVERT(decimal(5, 2), 123      )  -- 123.00
  3: SELECT TRY_CONVERT(decimal(5, 2), 123.456  )  -- 123.46
  4: SELECT TRY_CONVERT(decimal(5, 2), 1234.56  )  -- NULL
  5: SELECT TRY_CONVERT(decimal(5, 2), 123456E-3)  -- 123.46

Die Zeilen zeigen im Einzelnen:

  • Zeile 3: typisierte Dezimalzahl wird auf s gerundet (123.456 → 123.46).
  • Zeile 4: das Dezimal-Literal 1234.56 — von T-SQL als numeric(6, 2) typisiert — überschreitet p − s = 3 Vorkommastellen → NULL.
  • Zeile 5: wissenschaftliche Notation funktioniert als typisierte Zahl (123456E-3 → 123.456 → gerundet auf 123.46). Der Unterschied zur Text-Variante ist die Grundlage für das Zwei-Stufen-Pattern in der nächsten Sektion.

Sichere Typ-Konvertierung

„Sicher“ meint in dieser Serie: fehlertolerant — kein Abbruch, NULL statt Laufzeitfehler. Es meint nicht automatisch verlustfrei. Genau deshalb gehören zu diesem Pattern die Einschränkungen weiter unten.

Die Sektionen oben haben zwei Sonderfälle gezeigt, die im Import-Pfad explizit behandelt werden müssen:

  • Wissenschaftliche Notation als Text ('12345E-3') ergibt NULL. Workaround: erst zu float konvertieren (das toleriert die Notation), dann zu decimal (das setzt Präzision und Skala durch).
  • Eine leere Zeichenfolge wird vom direkten TRY_CONVERT(decimal, …) zwar zu NULL — sobald aber das Zwei-Stufen-Pattern via float läuft, gilt das nicht mehr: TRY_CONVERT(float, '') ergibt 0. Die leere Zeichenfolge muss daher vor dem float-Schritt explizit auf NULL gemappt werden.

Das folgende Beispiel löst beide Sonderfälle gemeinsam — als Parsing-Workaround, nicht als Universalweg (die Einschränkungen folgen direkt unter dem Beispiel):

  1: DECLARE @p_input AS nvarchar(30);
  2: SET @p_input = N'123,456';
  3: 
  4: SELECT TRY_CONVERT( decimal(5, 2)
  5:                   , TRY_CONVERT( float
  6:                                , REPLACE( CASE WHEN TRIM(@p_input) = ''
  7:                                                   THEN NULL
  8:                                                   ELSE @p_input
  9:                                              END
 10:                                         , ','
 11:                                         , '.'
 12:                                         )
 13:                                )
 14:                   ) AS [Output];  -- 123.46

Zwei Einschränkungen gehören zu diesem Pattern dazu:

  • Eingabegrammatik. Das Pattern normalisiert ausschließlich den Dezimaltrenner (Komma → Punkt). Tausendertrennzeichen ('1.234,56') und Währungszeichen — wie das  aus dem Eingangs-Beispiel — muss eine vorgelagerte Bereinigung entfernen, sonst bleibt das Ergebnis NULL. Auch Leerraum ist eng definiert: TRIM entfernt ohne weitere Angabe nur normale Leerzeichen, keine Tabs oder anderen Unicode-Leerraum.
  • Präzisionsgrenze. float (ohne Parameter float(53), IEEE-754 Double Precision) ist ein Näherungsdatentyp mit 15–17 signifikanten Dezimalstellen — nicht jede Dezimalzahl ist exakt darstellbar. Für die in diesem Artikel gezeigten Werte liefert der Umweg dasselbe decimal(5, 2)-Ergebnis wie eine direkte Konvertierung. Bei Geldbeträgen und anderen fachlich exakten Werten mit mehr signifikanten Stellen kann der float-Zwischenschritt den Wert dagegen verändern. Wenn Exaktheit zählt und keine wissenschaftliche Notation vorkommt, ist der direkte Weg TRY_CONVERT(decimal(p, s), …) ohne Zwischenschritt die bessere Wahl.

Wer das Pattern in einem ETL-Prozess in mehreren Spalten gleichzeitig braucht, abstrahiert es zu einer benutzerdefinierten Funktion fn_try_convert_decimal(@p_input nvarchar, @p_precision int, @p_scale int) — siehe Design Pattern // Sichere Typ-Konvertierung mit T-SQL.

Postgres-Brücke

Für Multi-Engine-Pipelines ist decimal die einfachste Brücke im ganzen TRY_CONVERT-Cluster: numeric(p, s) und decimal(p, s) decken in Postgres den SQL-Standard vollständig ab und erweitern ihn an einzelnen Stellen.

Drei Unterschiede gegenüber SQL Server:

  • Synonyme statt nur identisch. In Postgres sind numeric(p, s) und decimal(p, s) echte Synonyme — die Spaltendefinition speichert intern immer numeric. In SQL Server bleibt der ursprüngliche Bezeichner erhalten.
  • Deklarierte precision bis 1000. Explizit deklarierte numeric(p, s)-Typen erlauben p bis 1000 (vs. 38 in SQL Server). Seit Postgres 15 darf die deklarierte scale sogar negativ sein (−1000 bis 1000) — sie rundet dann auf Stellen links vom Komma. Ein unbeschränktes numeric ohne Parameter unterliegt deutlich größeren Implementierungsgrenzen: bis zu 131 072 Vorkommastellen und 16 383 Nachkommastellen.
  • Wissenschaftliche Notation als Text funktioniert. Postgres CAST('123456E-3' AS numeric(5, 2)) liefert 123.46. Das Zwei-Stufen-Pattern via float ist in Postgres also nicht nötig — die Notation wird direkt akzeptiert.

Postgres hat allerdings kein eingebautes try_cast (auch in Postgres 18, Release 2025-09-25 nicht). Ein PL/pgSQL-Wrapper liefert das NULL-statt-Exception-Verhalten:

  1: CREATE OR REPLACE FUNCTION fn_try_cast_numeric_5_2
  2: (
  3:     IN    p_input              text
  4: )
  5: RETURNS numeric(5, 2)
  6: LANGUAGE plpgsql
  7: IMMUTABLE
  8: AS $function$
  9: BEGIN
 10: 
 11:    IF p_input IS NULL OR TRIM(p_input) = '' THEN
 12:       RETURN NULL;
 13:    END IF;
 14: 
 15:    RETURN CAST(REPLACE(p_input, ',', '.') AS numeric(5, 2));
 16: 
 17: EXCEPTION
 18:    WHEN invalid_text_representation OR numeric_value_out_of_range THEN
 19:       RETURN NULL;
 20: END;
 21: $function$;
 22: 
 23: SELECT fn_try_cast_numeric_5_2('123,456');     -- 123.46
 24: SELECT fn_try_cast_numeric_5_2('123456E-3');   -- 123.46
 25: SELECT fn_try_cast_numeric_5_2('1234.56');     -- NULL
 26: SELECT fn_try_cast_numeric_5_2('');            -- NULL

Der EXCEPTION-Block fängt gezielt die zwei erwartbaren Fehlerklassen — ungültige Zahl-Darstellung (invalid_text_representation) und Wertebereichs-Überlauf (numeric_value_out_of_range). Ein pauschales WHEN OTHERS würde auch unerwartete Fehler still zu NULL machen. Dazu ein Kosten-Hinweis: Ein Block mit EXCEPTION-Teil ist laut Postgres-Doku deutlich teurer als einer ohne. Bei großen Datenmengen und vielen erwartbaren Fehlern lohnt der Blick auf set-basierte Vorprüfungen.

Zusammenfassung

  • decimal(p, s) und numeric(p, s) sind in SQL Server funktionell identisch — der Artikel verwendet decimal, alle Aussagen gelten 1:1 für numeric.
  • TRY_CONVERT(decimal(p, s), …) rundet Nachkommastellen still, lehnt aber überzählige Vorkommastellen mit NULL ab. Die Asymmetrie ist die zentrale Falle in ETL-Pipelines.
  • Bei Text-Eingangswerten: Komma → NULL, leere Zeichenfolge → NULL, wissenschaftliche Notation → NULL. Bei typisierten Zahlen funktioniert wissenschaftliche Notation. Daher das Zwei-Stufen-Pattern (text → float → decimal) — als Parsing-Workaround mit float-Präzisionsgrenze, nicht für exakte Werte mit vielen signifikanten Stellen.
  • Postgres-Pendant: numeric(p, s) oder decimal(p, s) — gleiche Wertebereiche bis p = 38, darüber hinaus bis p = 1000. Wissenschaftliche Notation als Text wird direkt akzeptiert, das Zwei-Stufen-Pattern ist dort nicht nötig.

Für die Praxis als Entscheidungshilfe:

EingangslageEmpfohlener Weg
Punkt-Notation, Skala bekanntdirekt TRY_CONVERT(decimal(p, s), …)
Komma als DezimaltrennzeichenDezimaltrenner normalisieren, dann TRY_CONVERT
Wissenschaftliche Notation als TextZwei-Stufen-Pattern — nur bei akzeptablem Präzisionsrisiko
Geldbeträge und andere fachlich exakte Wertekein float-Zwischenschritt — Eingabe vorab validieren
ETL-FehlerdiagnoseRohwert und konvertierten Wert getrennt speichern, Fehlerklasse protokollieren

FAQ

decimal oder numeric — was nehmen?

Beide sind in SQL Server Synonyme und funktionell identisch. Welcher Bezeichner verwendet wird, ist Konvention: decimal ist in T-SQL-Communitys häufiger anzutreffen, numeric in der ANSI-Standard-Doku. Tools wie der SSMS-Designer defaulten oft auf decimal.

Warum gibt TRY_CONVERT(decimal(5, 2), '123,45') NULL zurück?

Weil das Komma in SQL Server nicht als Dezimaltrennzeichen erkannt wird — TRY_CONVERT erwartet ausschließlich einen Punkt. Bei deutschen Eingangsdaten muss das Komma vor der Konvertierung durch einen Punkt ersetzt werden. Das sichere Konvertierungs-Pattern oben übernimmt das mit REPLACE.

Rundet TRY_CONVERT(decimal, …) oder lehnt es ab? 

Beides — je nachdem, ob das Problem im Nachkomma oder im Vorkomma liegt. Nachkommastellen werden still auf s gerundet (kaufmännisch). Vorkomma-Overflow liefert NULL. Diese Asymmetrie ist in ETL-Pipelines wichtig: ein NULL im Ziel-Feld bedeutet bei decimal nicht zwangsläufig „Quelle hat keinen Wert geliefert“, sondern kann auch „Quelle hatte zu viele Vorkommastellen“ heißen.

Warum liefert TRY_CONVERT(decimal, …) NULL, obwohl die Zahl gültig aussieht? 

Meist steckt Vorkomma-Overflow dahinter: Die Zahl ist syntaktisch korrekt, hat aber mehr als p − s Vorkommastellen. Eine Gegenprobe mit größerer Precision unterscheidet die Fälle. Liefert TRY_CONVERT(decimal(38, 2), …) einen Wert, war es Overflow. Bleibt auch die Gegenprobe NULL, ist schon die Zahl-Darstellung ungültig. Für ETL-Prüfungen empfiehlt es sich, Rohwert und konvertierten Wert getrennt zu speichern und die Fehlerklasse mit zu protokollieren.

Wie konvertiert man wissenschaftliche Notation ('12345E-3') sicher zu decimal? 

Das Zwei-Stufen-Pattern aus der Sektion „Sichere Typ-Konvertierung“ — erst zu float (das toleriert die Notation als Text), dann zu decimal (das setzt Präzision/Skala durch). Der float-Zwischenschritt ist dabei ein Parsing-Workaround: Bei mehr als rund 15 signifikanten Stellen kann er den Wert verändern (siehe die Einschränkungen in der Sektion). Querverweis: Datenqualität in SQL Server // TRY_CONVERT für float und real sicher anwenden.

TRY_CAST oder TRY_CONVERT für decimal? 

Beide liefern das NULL-statt-Fehler-Verhalten und sind für diesen Anwendungsfall austauschbar. TRY_CONVERT bietet zusätzlich den optionalen style-Parameter — für decimal spielt er keine Rolle, bei Datums- und Geld-Konvertierungen dagegen schon. Wer nah am SQL-Standard bleiben will, nimmt TRY_CAST. Den Vergleich aller vier Funktionen liefert Datenqualität // Grundlagen der Typ-Konvertierung mit T-SQL.

Postgres-Pendant für TRY_CONVERT(decimal, …)?

CAST(s AS numeric(p, s)) oder CAST(s AS decimal(p, s)) — Postgres macht keinen Unterschied. Für NULL-statt-Exception-Verhalten ist ein PL/pgSQL-Wrapper nötig (Postgres hat kein eingebautes try_cast, Stand 18.0). Postgres ist außerdem liberaler bei Text-Inputs: wissenschaftliche Notation wird direkt akzeptiert.

Verwandte Artikel

ETL-Kontext:

TRY_CONVERT für andere Datentypen:

Grundlagen: