Datenqualität beginnt bei der Typ-Konvertierung — und bei Integer-Spalten zeigt TRY_CONVERT ein paar Eigenheiten, die im Import-Pfad gerne übersehen werden: eine leere Zeichenfolge wird zu 0 (statt zu NULL), jedes Dezimal- oder Tausendertrennzeichen führt zu einem NULL, und eine typisierte Dezimalzahl wird stillschweigend abgeschnitten — ohne Rundung. Dieser Artikel sortiert die Regeln, zeigt das sichere Konvertierungs-Pattern und reicht eine Postgres-Brücke nach.
Kurzüberblick:
- Vier Integer-Typen decken klar abgestufte Wertbereiche ab — vom 1-Byte-
tinyint(0..255, unsigned) bis zum 8-Byte-bigint. - Bei der Konvertierung aus Text akzeptiert
TRY_CONVERTnur ganzzahlige Darstellungen — Vorzeichen und Whitespace sind erlaubt, Dezimal- oder Tausendertrennzeichen führen Locale-unabhängig zuNULL. - Eine leere Zeichenfolge ist der Sonderfall: sie liefert
0, was fachlich meist falsch ist — einCASE/TRIM-Pattern macht daraus sauberNULL. - In Postgres wirft
CAST(... AS bigint)eine Exception, stattNULLzurückzugeben; ein eigener PL/pgSQL-Wrappertry_cast_bigintschließt die Lücke.
Voraussetzung: SQL Server 2017+ (TRY_CONVERT ist seit 2012 verfügbar; das sichere Pattern weiter unten verwendet TRIM, das ab SQL Server 2017 zur Verfügung steht — auf 2012–2016 stattdessen LTRIM(RTRIM(...))). Beispiele sind ohne Sample-Datensatz lauffähig — reine Inline-Literale.
Inhalt
- Wertbereich der Integer-Typen
- Text
- Kein Text
- Sichere Typ-Konvertierung
- Postgres-Brücke
- Zusammenfassung
- FAQ
- Verwandte Artikel
Die Microsoft-Dokumentation zu TRY_CONVERT (bzw. CAST und CONVERT) gibt an, dass der zu konvertierende Wert ein beliebiger Ausdruck sein kann. Damit kommen sowohl nvarchar-Texte aus CSV-/JSON-/XML-Imports als auch typisierte Zahlen (z. B. decimal(18,5) aus einer Pipeline-Berechnung) als Input in Frage. Der Artikel unterscheidet zwischen den beiden Fällen — Text und Kein Text — und stellt für beide ein sicheres Konvertierungs-Pattern vor.
Wertbereich der Integer-Typen
Vor der Konvertierung steht die Typ-Wahl. Die vier Integer-Typen in SQL Server unterscheiden sich nur durch Wertebereich und Speicherbedarf — die Konvertierungs-Regeln sind identisch.
| Datentyp | Min | Max | Bytes | Typische Verwendung |
|---|---|---|---|---|
bigint | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 807 | 8 | Riesige Surrogate-Keys, Counter über Milliarden, Snowflake-IDs |
int | -2 147 483 648 | 2 147 483 647 | 4 | Standard-Surrogate-Key, Mengen, Counter im 9-stelligen Bereich |
smallint | -32 768 | 32 767 | 2 | Jahre, Klein-Mengen, ältere Lookup-Schlüssel |
tinyint | 0 | 255 | 1 | Flags, Status-Codes, kleine Lookup-Werte — unsigned! |
tinyint ist eine Microsoft-Eigenheit und ist der einzige SQL-Server-Integer-Typ ohne Vorzeichen. Ein negativer Wert lässt sich nicht in tinyint konvertieren, der TRY_CONVERT-Aufruf gibt NULL zurück. Postgres kennt diesen Typ gar nicht — kleinster Integer dort ist smallint (signed, -32 768..32 767).
Text
Wenn ein Text in einen Integer-Wert konvertiert werden soll, sind unter anderem die folgenden Szenarien zu untersuchen:
- Der Text enthält eine gültige Zahl
- Die Zahl enthält ein Komma als Dezimaltrennzeichen
- Der Text enthält Tausendertrennzeichen und ggf. Dezimaltrennzeichen
- Der Text ist eine leere Zeichenfolge oder enthält nur Leerzeichen
Die folgenden Aufrufe von TRY_CONVERT decken diese Fälle ab. Hinter den Anweisungen ist das jeweils zurückgegebene Ergebnis notiert:
1: SELECT TRY_CONVERT(int, NULL ) -- NULL
2: SELECT TRY_CONVERT(int, N'123' ) -- 123
3: SELECT TRY_CONVERT(int, N'123,4') -- NULL
4: SELECT TRY_CONVERT(int, N'1,234') -- NULL
5: SELECT TRY_CONVERT(int, N'123.4') -- NULL
6: SELECT TRY_CONVERT(int, N'1.234') -- NULL
7: SELECT TRY_CONVERT(int, N'' ) -- 0
8: SELECT TRY_CONVERT(int, N' ' ) -- 0
9: SELECT TRY_CONVERT(int, N' 123' ) -- 123
10: SELECT TRY_CONVERT(int, N'123 ' ) -- 123
Zahlen, die offensichtlich ganzzahlig sind, werden wie erwartet konvertiert. Führende und folgende Leerzeichen beeinflussen das Ergebnis nicht. Dezimalzahlen mit Trennzeichen — egal ob Komma (deutsche Notation) oder Punkt (amerikanische Notation) — können nicht in int konvertiert werden und liefern NULL. TRY_CONVERT ist Locale-unabhängig: weder SET LANGUAGE noch SET DATEFORMAT beeinflussen die Interpretation eines Dezimal- oder Tausendertrennzeichens.
Damit hält das Ergebnis bereits eine Überraschung bereit. Leere Zeichenfolgen oder Zeichenfolgen, die nur aus Leerzeichen bestehen, werden zum Wert 0 konvertiert. Während das NULL-Ergebnis bei Dezimaltrennzeichen nachvollziehbar ist, ist die 0-Konvertierung einer leeren Zeichenfolge im ETL-Kontext fachlich (meistens) falsch: ein leeres Feld einer CSV-Spalte ist semantisch ein „unbekannt“, kein „0″. Die sichere Variante ist weiter unten als Pattern beschrieben.
Kein Text
Wenn der Eingangswert nicht als Text vorliegt, sondern bereits typisiert ist (Ganzzahl mit anderem Wertebereich oder Dezimalzahl), reduzieren sich die Szenarien auf:
- Der übergebene Wert ist eine Ganzzahl (ggf. außerhalb des Ziel-Wertebereichs).
- Der übergebene Wert ist eine Dezimalzahl.
Die ersten drei Tests:
1: SELECT TRY_CONVERT(int, 2147483648) -- NULL
2: SELECT TRY_CONVERT(int, 123) -- 123
3: SELECT TRY_CONVERT(int, 1234.5) -- 1234
Die erste Zahl ist um 1 größer als int-MAX (2 147 483 647). Der Aufruf liefert erwartungsgemäß NULL. Hierbei handelt es sich um einen typischen Overflow-Schutz. Dezimalzahlen können hingegen konvertiert werden, aber SQL Server rundet nicht: TRY_CONVERT(int, 1234.5) liefert 1234, nicht 1235. Wer Rundung will, muss sie vor dem Konvertieren explizit erzwingen — ROUND(1234.5, 0) liefert 1235.0, das anschließend zu 1235 konvertiert.
Die Wertbereich-Grenzen pro Typ liefert ein zweiter Block:
1: SELECT TRY_CONVERT(smallint, 32767); -- 32767 (smallint-MAX)
2: SELECT TRY_CONVERT(smallint, 32768); -- NULL (Overflow)
3: SELECT TRY_CONVERT(tinyint, 255); -- 255 (tinyint-MAX, unsigned)
4: SELECT TRY_CONVERT(tinyint, 256); -- NULL (Overflow)
5: SELECT TRY_CONVERT(tinyint, -1); -- NULL (tinyint ist unsigned)
Sichere Typ-Konvertierung
Die zwei Blöcke oben haben zwei Sonderfälle gezeigt, die im Import-Pfad explizit behandelt werden müssen:
- Eine leere Zeichenfolge wird zu
0konvertiert. Wenn das fachlich falsch ist (Default-Fall bei CSV-Imports), muss die leere Zeichenfolge vor demTRY_CONVERTaufNULLgemappt werden. - Eine typisierte Dezimalzahl wird zum ganzzahligen Anteil trunkiert, nicht gerundet. Falls Rundung gewünscht ist, muss vor dem
TRY_CONVERTeinROUND(...,0)angewendet werden — oder der Ziel-Typ ist aufdecimal(p, 0)stattintzu setzen, wobei SQL Server dort still rundet (siehe FAQ).
Anwendungs-Beispiel mit DECLARE und snake_case-Variablen-Konvention:
1: DECLARE @p_input AS nvarchar(30);
2: SET @p_input = N'123';
3:
4: SELECT TRY_CONVERT(int,
5: CASE WHEN TRIM(@p_input) = '' THEN NULL ELSE @p_input END
6: ) AS [Output];
Wer das Pattern in einem ETL-Prozess in mehreren Spalten gleichzeitig braucht, abstrahiert es zu einer benutzerdefinierten Funktion fn_try_convert_int(@p_input nvarchar) — siehe Design Pattern // Sichere Typ-Konvertierung mit T-SQL.
Postgres-Brücke
In Multi-Engine-ETL-Pipelines stellt sich die Frage nach dem Postgres-Pendant. Zwei Unterschiede zum SQL-Server-Stack sind entscheidend:
CAST(s AS bigint)ist Postgres‘ Default-Konvertierung — und wirft eine Exception bei ungültigem Input. Es gibt also kein direktesTRY_CONVERT-Pendant; der Aufruf läuft entweder durch oder bricht ab.- Auch Postgres 18 (Release 2025-09-25) hat kein eingebautes Pendant nachgezogen — weder eine
try_cast-Funktion noch die SQL/JSONCAST ... ON ERROR NULL-Syntax aus SQL:2023 sind Teil von 18.0. Den NULL-statt-Exception-Wrapper schreibt man also weiterhin selbst als PL/pgSQL-Funktion.
Wrapper-Funktion als PL/pgSQL-Pendant zu TRY_CONVERT(int, …):
1: CREATE OR REPLACE FUNCTION try_cast_bigint (p_input text)
2: RETURNS bigint
3: LANGUAGE plpgsql
4: IMMUTABLE
5: AS $$
6: BEGIN
7: IF p_input IS NULL OR TRIM(p_input) = '' THEN
8: RETURN NULL;
9: END IF;
10: RETURN p_input::bigint;
11: EXCEPTION
12: WHEN invalid_text_representation OR numeric_value_out_of_range
13: THEN
14: RETURN NULL;
15: END;
16: $$;
Damit verhält sich try_cast_bigint('1,234') wie TRY_CONVERT(int, '1,234'): der Aufruf gibt NULL zurück, statt eine Exception zu werfen. Analog für int, smallint — Postgres hat keinen tinyint-Typ; der kleinste Integer-Typ dort ist smallint (signed, -32 768..32 767).
Wenn die Eingabe formatierte Zahlen mit Tausender-/Dezimaltrennzeichen liefert (deutsche oder US-Notation), greift to_number mit einem Format-Pattern:
1: SELECT to_number('1.234,56', 'FM999G999D99'); -- 1234.56 (deutsche Notation, lc_numeric = 'de_DE.UTF-8')
2: SELECT to_number('1,234.56', 'FM999G999D99'); -- 1234.56 (US-Notation, lc_numeric = 'en_US.UTF-8')
to_number ist Locale-abhängig über die Session-Variable lc_numeric — anders als SQL-Servers TRY_CONVERT. Im ETL-Kontext lohnt es sich, lc_numeric pro Session explizit zu setzen, damit der Import von der Server-Default-Konfiguration entkoppelt ist.
SQL-Server TRY_CONVERT | Postgres-Pendant | Bemerkung |
|---|---|---|
TRY_CONVERT(bigint, s) | try_cast_bigint(s) (Wrapper) | NULL statt Exception |
TRY_CONVERT(int, s) | try_cast_int(s) (Wrapper) | analog |
TRY_CONVERT(smallint, s) | try_cast_smallint(s) (Wrapper) | analog |
TRY_CONVERT(tinyint, s) | — | Postgres hat keinen tinyint-Typ |
TRY_CONVERT(int, s) mit Trennzeichen | to_number(s, 'FM999G999') | Locale-abhängig über lc_numeric |
Zusammenfassung
TRY_CONVERT ist für Integer-Imports der Default — solange die Sonderfälle bekannt sind. Eine leere Zeichenfolge wird zu 0 konvertiert (oft fachlich falsch), Dezimalzahlen werden trunkiert (nicht gerundet), Dezimaltrennzeichen führen zu NULL (Locale-unabhängig). Im sicheren Pattern wird die leere Zeichenfolge per CASE/TRIM auf NULL gemappt; Rundung kommt vor dem TRY_CONVERT als ROUND(...,0).
Take-Away:
- Typ-Wahl nach erwartetem Wertebereich:
tinyint(0..255) für Flags,int(≈ 2,1 Mrd.) für die meisten Standardfälle,bigintfür alles, was nicht inintpasst. TRY_CONVERTals Default für CSV-/JSON-/XML-Imports — kein Laufzeitfehler, sondern stillerNULL-Fallback.- Leere Zeichenfolge ist im Import meist
NULL-Semantik, nicht0-Semantik — sicheres Pattern aus „Sichere Typ-Konvertierung“ anwenden. - Postgres-Brücke über einen PL/pgSQL-Wrapper
try_cast_bigint(analog fürint,smallint);to_numberfür formatierte Zahlen mitlc_numeric-Bewusstsein.
FAQ
Warum gibt TRY_CONVERT(int, '1,234') NULL zurück?
TRY_CONVERT ist Locale-unabhängig und kennt weder Dezimal- noch Tausendertrennzeichen. Der Aufruf erwartet eine reine Ziffernfolge (optional mit Vorzeichen, führende und nachfolgende Leerzeichen werden toleriert). Sobald ein Komma oder ein Punkt im String vorkommt, scheitert die Konvertierung — unabhängig von SET LANGUAGE oder SET DATEFORMAT. Wenn die Quelle deutsche Notation liefert ('1.234,56'), vor dem Aufruf per REPLACE säubern: REPLACE(REPLACE(@p_input, '.', ''), ',', '.') macht aus '1.234,56' ein '1234.56', das anschließend per TRY_CONVERT(decimal(18,2), …) konvertiert.
int oder bigint — welcher Typ wann?
int reicht für jeden Wertebereich bis ca. ±2,1 Milliarden — Surrogate-Keys, Counter, Mengen. bigint kommt zum Einsatz, sobald der Counter über die int-Grenze hinauswächst (z. B. globale ID-Generatoren, Twitter-/Snowflake-IDs) oder Speicher-Effizienz keine Rolle spielt. Die Faustregel: int als Default, bigint nur, wenn explizit nachweisbar ist, dass der Wertebereich gesprengt wird. smallint und tinyint lohnen sich nur in Tabellen mit Milliarden Zeilen, wo Byte-Sparsamkeit messbar wird — sonst ist die Wartbarkeits-Last (Overflow-Risiko beim Schema-Wachstum) nicht gerechtfertigt.
Wie erzwinge ich Rundung statt Trunkierung beim Konvertieren von 1234.5?
TRY_CONVERT(int, 1234.5) liefert 1234 — Trunkierung. Für kaufmännische Rundung vorher explizit ROUND einschieben: TRY_CONVERT(int, ROUND(1234.5, 0)) liefert 1235. Achtung: decimal(p, 0) ist kein strikter Ersatz für int — SQL Server rundet bei der Skala-Reduktion still (TRY_CONVERT(decimal(10, 0), 1234.5) liefert 1235, nicht NULL und auch keinen Fehler). Eine Konvertierung, die den Verlust von Nachkommastellen out-of-the-box als NULL signalisiert, gibt es in SQL Server nicht; nur SET NUMERIC_ROUNDABORT ON macht aus dem stillen Rundungsschritt einen Fehler (Default: OFF).
Was tun, wenn die CSV-Spalte gemischt Komma und Punkt als Dezimaltrennzeichen enthält?
Vor dem TRY_CONVERT per REPLACE normalisieren. Wenn klar ist, dass Komma=Dezimaltrennzeichen und Punkt=Tausendertrennzeichen (deutsche Notation): REPLACE(REPLACE(@p_input, '.', ''), ',', '.'). Wenn die Spalte beide Notationen gemischt liefert, lässt sich das nicht zuverlässig per Regex unterscheiden ('1,234' ist mehrdeutig) — dann ist die Eingangs-Datenquelle das Problem, nicht der Konverter. Im Zweifel: pro Datensatz dokumentieren, welches Locale die Quelle nutzt.
Postgres-Pendant für TRY_CONVERT(int, …)?
Es gibt kein direktes Pendant — CAST(s AS int) wirft Exception statt NULL, und auch Postgres 18 (Release 2025-09-25) hat weder eine eingebaute try_cast-Funktion noch die SQL/JSON CAST ... ON ERROR NULL-Syntax nachgezogen. Lösung daher (Stand 18.0): PL/pgSQL-Wrapper try_cast_int(p_input text) RETURNS int mit EXCEPTION WHEN invalid_text_representation OR numeric_value_out_of_range THEN RETURN NULL (siehe Postgres-Brücke). Für formatierte Zahlen mit Tausender-/Dezimaltrennzeichen ist to_number(s, format) der Postgres-Weg, allerdings Locale-abhängig über lc_numeric — anders als SQL-Servers Locale-Unabhängigkeit.
Verwandte Artikel
- Datenqualität in einem ETL-Prozess
- Design Pattern // Architektur eines ETL-Prozesses
- Datenqualität // Grundlagen der Typ-Konvertierung mit T-SQL
- Design Pattern // Sichere Typ-Konvertierung mit T-SQL
- TRY_CONVERT // Konvertierung nach decimal, numeric
- TRY_CONVERT // Konvertierung nach money, smallmoney
- TRY_CONVERT // Konvertierung nach float, real
- TRY_CONVERT // Konvertierung nach bit
- TRY_CONVERT // Konvertierung nach date, datetime, datetime2, time
1 Kommentar zu „Datenqualität in SQL Server // TRY_CONVERT für bigint, int, smallint und tinyint sicher anwenden“
Die Kommentare sind geschlossen.