Datenqualität in SQL Server // TRY_CONVERT für bit sicher anwenden — Ja/Nein-Werte konvertieren

Wer einmal eine Ja/Nein-Spalte aus einem Legacy-Export übernommen hat, kennt den Befund: Die Quelle liefert 'J''ON' oder ein schlichtes 'x', und TRY_CONVERT(bit, N'J') quittiert die deutsche Notation mit NULL. Eingebaut versteht SQL Server beim bit-Ziel nur Integer-Strings und die Literale 'true'/'false' — jede andere Yes/No-Notation braucht eine eigene Zuordnung.

Auf einen Blick:

  • Ein bit speichert einen Ja/Nein-Wert: 01 und NULL als dritter Zustand (dreiwertige Logik). SQL Server speichert bit-Spalten platzsparend und packt bis zu 8 bit-Spalten einer Zeile in ein Byte.
  • TRY_CONVERT(bit, …) versteht Integer-Strings nach der Regel „alles außer 0 ergibt 1“ und die Literale 'true'/'false' — case-insensitiv und Locale-unabhängig. Die stille Falle: Eine leere Zeichenfolge wird zu 0 konvertiert, nicht zu NULL (gemessen auf SQL Server 2019 und 2022, dokumentiert ist dieser Fall nicht).
  • Die eigene Funktion [dbo].[fn_convert_bit] mappt Yes/No-Notationen ('J''JA''ON''x', …) über eine explizite Positivliste (CASE) auf 0/1. Unbekannte Werte und die leere Zeichenfolge behandelt sie bewusst als NULL.
  • Postgres-Kernbefund: boolean ist dort nativer Typ, und der Cast s::boolean akzeptiert von Haus aus mehr Yes/No-Formen als SQL Server. Bei ungültigem Input wirft er allerdings eine Exception — das Try-Verhalten liefert erst der Wrapper fn_try_cast_boolean.

Voraussetzung: TRY_CONVERT existiert seit SQL Server 2012. Die Funktion fn_convert_bit nutzt TRIM und braucht daher SQL Server 2017+ (davor LTRIM(RTRIM(…))-Fallback). Alle Beispiele sind ohne Sample-Datenbank lauffähig. Die Postgres-Beispiele setzen keine bestimmte Version voraus — nur die erwähnte Vorprüfung mit pg_input_is_valid braucht Postgres 16.

Inhalt

Datentyp bit — Wertebereich und Speicherbedarf

EigenschaftWert
Wertebereich01 und NULL (dreiwertige Logik: 0, 1, „unbekannt“)
SpeicherbedarfSQL Server speichert bit-Spalten platzsparend und packt bis zu 8 bit-Spalten einer Zeile in 1 Byte.
Vergleich mit booleanSQL Server besitzt keinen nativen BOOLEAN-Datentyp für Spalten oder Variablen. Boolesche Ausdrücke existieren nur intern in Prädikaten und Bedingungen (IS NULLEXISTS, …). Postgres hat boolean als nativen Datentyp.

Die NULL-Möglichkeit ist im Import-Kontext relevant: Ein leerer CSV-Wert ist semantisch meist „unbekannt“ und sollte als NULL landen, nicht als 0. Genau das leistet der eingebaute Konverter aber nicht — er macht aus der leeren Zeichenfolge eine 0 (nächste Sektion). Erst die eigene Funktion weiter unten mappt sie konsequent auf NULL.

Text nach bit konvertieren

Text-Eingangswerte kommen typischerweise aus CSV-, JSON- und XML-Importen. Wird ein solcher nvarchar-/varchar-Wert an TRY_CONVERT übergeben, akzeptiert die bit-Konvertierung zwei Eingangs-Kategorien: Integer-Strings, abgebildet nach der Regel „alles außer 0 ergibt 1“, und die Literale 'true'/'false'. Umgebende Leerzeichen sind erlaubt. Jede andere Notation ergibt NULL.

  1: SELECT TRY_CONVERT(bit, NULL     )  -- NULL
  2: SELECT TRY_CONVERT(bit, N'1'     )  -- 1
  3: SELECT TRY_CONVERT(bit, N'0'     )  -- 0
  4: SELECT TRY_CONVERT(bit, N'-1'    )  -- 1
  5: SELECT TRY_CONVERT(bit, N'42'    )  -- 1
  6: SELECT TRY_CONVERT(bit, N'true'  )  -- 1
  7: SELECT TRY_CONVERT(bit, N'FALSE' )  -- 0
  8: SELECT TRY_CONVERT(bit, N' 1 '   )  -- 1
  9: SELECT TRY_CONVERT(bit, N'1.5'   )  -- NULL
 10: SELECT TRY_CONVERT(bit, N'J'     )  -- NULL
 11: SELECT TRY_CONVERT(bit, N'YES'   )  -- NULL
 12: SELECT TRY_CONVERT(bit, N'ON'    )  -- NULL
 13: SELECT TRY_CONVERT(bit, N'x'     )  -- NULL
 14: SELECT TRY_CONVERT(bit, N''      )  -- 0
 15: SELECT TRY_CONVERT(bit, N' '     )  -- 0
 16: SELECT TRY_CONVERT(bit, N'-'     )  -- 0

Die Zeilen zeigen im Einzelnen:

  • Zeilen 2–5: Integer-Strings folgen der Regel „alles außer 0 ergibt 1“. Auch '-1' und '42' werden zu 1. Das ist dokumentierte Semantik: Jeder Nicht-Null-Wert wird beim bit-Ziel zu 1, eine Wertebereichs-Prüfung findet nicht statt.
  • Zeilen 6–7: Die Literale 'true'/'false' sind case-insensitiv — das gilt auch unter einer case-sensitiven oder binären Kollation (gemessen mit COLLATE Latin1_General_CS_AS und Latin1_General_BIN2). Die Konvertierung ist außerdem Locale-unabhängig: SET LANGUAGE ändert an keinem Ergebnis dieses Blocks etwas.
  • Zeile 8: Umgebende Leerzeichen sind erlaubt. Das gilt nur für das normale Leerzeichen — mit einem Tab an derselben Stelle lieferte die Messung NULL.
  • Zeile 9: Dezimal-Darstellungen als Text scheitern, auch ein harmlos wirkendes '1.0'. Der Parser akzeptiert nur ganzzahlige Darstellungen.
  • Zeilen 10–13: Yes/No-Notationen kennt der Parser nicht. Deutsche 'J', englische 'YES', die Switch-Notation 'ON' und der historische Marker 'x' fallen alle auf NULL.
  • Zeilen 14–16: Die leere Zeichenfolge, ein String nur aus Leerzeichen und ein einzelnes Vorzeichen ('-''+') werden zu 0 konvertiert. Das ist gemessenes Verhalten (SQL Server 2019 und 2022, identisch für CASTTRY_CAST und TRY_CONVERT) — die Microsoft-Dokumentation legt diesen Fall nicht fest. Die Integer-Ziele zeigen dasselbe Ergebnis: TRY_CONVERT(int, N'') liefert ebenfalls 0, siehe Datenqualität in SQL Server // TRY_CONVERT für bigint, int, smallint und tinyint sicher anwenden.

Kernaussage: Ein leeres Feld ist im Import semantisch ein „unbekannt“, TRY_CONVERT(bit, …) macht daraus aber den fachlichen Wert 0 — still, ohne Fehler, ohne NULL. Ein '0' aus der Quelle und ein leeres Feld sind im Ziel danach nicht mehr unterscheidbar. Wo das falsch ist (der Regelfall bei CSV-Importen), mappt das Pattern aus Sichere Typ-Konvertierung die leere Zeichenfolge vorab auf NULL.

Typisierte Zahlen nach bit konvertieren

Kommt der Eingangswert bereits typisiert an, etwa als Integer, decimalfloat oder money, reduziert sich das Verhalten auf eine einzige Regel: 0 wird zu 0, jeder andere Wert wird zu 1.

  1: SELECT TRY_CONVERT(bit, 1)                    -- 1
  2: SELECT TRY_CONVERT(bit, 0)                    -- 0
  3: SELECT TRY_CONVERT(bit, -1)                   -- 1
  4: SELECT TRY_CONVERT(bit, 42)                   -- 1
  5: SELECT TRY_CONVERT(bit, 0.5)                  -- 1
  6: SELECT TRY_CONVERT(bit, 123456E-3)            -- 1
  7: SELECT TRY_CONVERT(bit, CAST(0.50 AS money))  -- 1
  8: SELECT TRY_CONVERT(bit, CAST(0.00 AS money))  -- 0

Die Zeilen zeigen im Einzelnen:

  • Zeilen 3–4: Vorzeichen und Größe spielen keine Rolle — -1 und 42 werden gleichermaßen zu 1. Eine Wertebereichs-Prüfung findet nicht statt, auch der bigint-Maximalwert wird zu 1.
  • Zeilen 5–6: Dezimalzahlen und float-Literale in wissenschaftlicher Notation werden weder gerundet noch trunkiert. Die „alles außer 0“-Regel greift vor jeder Stellen-Betrachtung: 0.5 wird zu 1.
  • Zeilen 7–8: Auch money folgt der Regel — nur die exakte 0 ergibt 0.

Kernaussage: Die Konvertierung typisierter Zahlen nach bit kennt weder Rundung noch Trunkierung noch eine Wertebereichs-Grenze, nur die dokumentierte Abbildung „0 bleibt 0, alles andere wird 1“. Das Ergebnis kann von der Integer-Intuition abweichen: TRY_CONVERT(int, 0.5) trunkiert zu 0TRY_CONVERT(bit, 0.5) liefert 1. Auch eine strenge 0/1-Validierung leistet die Konvertierung nicht — 42 wird still zu 1, ohne Hinweis auf den unerwarteten Eingangswert. Wenn fachlich nur exakt 0 und 1 gültig sind oder eine Schwelle gemeint ist („ab x gilt Ja“), gehört diese Prüfung als explizite Vergleichs-Logik in die Abfrage, nicht in die Typ-Konvertierung.

Sichere Typ-Konvertierung

„Sicher“ meint in dieser Serie fehlertolerant: kein Abbruch, NULL statt Laufzeitfehler. Diese Eigenschaft bringt TRY_CONVERT(bit, …) von Haus aus mit. Semantisch korrekt ist das Ergebnis damit noch nicht: TRY_CONVERT beantwortet die Frage, ob SQL Server einen Wert konvertieren kann — nicht die Frage, ob der Wert fachlich gültig ist. Zwei Sonderfälle aus den Sektionen oben brauchen im Import-Pfad deshalb eine explizite Behandlung:

  • Leere Zeichenfolge → 0: Ein leeres CSV-Feld steht meist für „unbekannt“ und gehört als NULL ins Ziel, nicht als fachliches „Nein“. Die leere Zeichenfolge muss deshalb vor dem TRY_CONVERT auf NULL gemappt werden — dasselbe Pattern wie bei den Integer-Typen.
  • Unbekannte Notation → NULL: 'J''YES' oder 'x' fallen still auf NULL. Damit verschmelzen drei verschiedene Sachverhalte im selben Ziel-NULL: Die Quelle hat NULL geliefert, die Quelle hat ein leeres Feld geliefert (nach dem Vorab-Mapping), oder die Notation ist unbekannt. Ein NULL im Ziel ist deshalb keine ausreichende Fehlerdiagnose. Wer die Fälle unterscheiden muss, speichert Rohwert und konvertierten Wert getrennt oder führt eine eigene Konvertierungs-Status-Spalte mit (Gegenprobe: siehe FAQ).

Das Anwendungs-Beispiel löst den ersten Sonderfall:

  1: DECLARE @p_input AS nvarchar(30);
  2: SET @p_input = N'';
  3: 
  4: SELECT TRY_CONVERT( bit
  5:                   , CASE WHEN TRIM(@p_input) = '' THEN NULL ELSE @p_input END
  6:                   ) AS [Output];  -- NULL statt 0

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

  • Eingabegrammatik: TRIM entfernt ohne weitere Angabe nur das normale Leerzeichen (char(32)). Tabs oder anderen Unicode-Leerraum erkennt das Pattern nicht als „leer“, und der bit-Parser hat sie in der Messung ebenfalls nicht akzeptiert — solche Werte enden als NULL. Können sie in den Quelldaten vorkommen, muss die Normalisierung sie explizit behandeln.
  • Die Notations-Lücke bleibt: Das Pattern behebt nur den Leere-Zeichenfolge-Fall. Yes/No-Notationen jenseits von Integer-Strings und 'true'/'false' brauchen eine eigene Zuordnung — die nächste Sektion.

Genau diese Zuordnung übernimmt [dbo].[fn_convert_bit], und sie behandelt die leere Zeichenfolge bewusst als NULL-Fall: Nach dem TRIM fällt sie durch die CASE-Liste auf ELSE NULL — das ist Vertragslogik der Funktion, nicht Verhalten des SQL-Server-Parsers. Wie sich das Pattern in einen ETL-Prozess mit Materialisierung und Fehler-Identifikation einbettet, zeigt Design Pattern // Sichere Typ-Konvertierung mit T-SQL.

[dbo].[fn_convert_bit] — eigene Funktion für Nicht-Standard-Notationen

In ETL-Pipelines tauchen aus Legacy-Quellen Yes/No-Notationen auf, die TRY_CONVERT(bit, …) nicht abbildet: deutsche 'J'/'JA'/'N'/'NEIN', englische 'Y'/'YES'/'NO', Switch-Notationen 'ON'/'OFF', dazu Markierungen aus manchen Legacy-Beständen, in denen 'x' für „gesetzt“ und '-' für „nicht gesetzt“ steht. Gerade der Bindestrich ist quellspezifisch: In anderen Beständen bedeutet '-' „fehlend“ oder „nicht anwendbar“ — dann gehört er nicht auf 0, sondern auf NULL. Die folgende Funktion mappt die Notationen ihrer Quelle über eine explizite Positivliste deterministisch auf 0/1 und liefert NULL für unbekannte Werte.

Beschreibung

Konvertiert einen übergebenen Eingangswert in den Zieldatentyp bit. Kann der Eingangswert nicht zugeordnet werden, wird NULL zurückgegeben. Der übergebene Wert wird vor dem Vergleich mit UPPER normalisiert. Die vorgesehenen ASCII-Notationen sind damit case-insensitiv, bei Nicht-ASCII-Zeichen ist UPPER kollationsabhängig. Leerzeichen am Anfang und Ende werden ignoriert (TRIM). Eine leere Zeichenfolge fällt nach dem TRIM durch die CASE-Liste und wird zu NULL — anders als beim direkten TRY_CONVERT, das 0 liefert.

Syntax

  1: [dbo].[fn_convert_bit](@p_value AS nvarchar(50))

Argumente

  • p_value — der umzuwandelnde Eingangswert. Parameter-Typ ist nvarchar(50) statt der knapperen nvarchar(5) aus der Erstfassung. Längere Eingaben würden am Parameter sonst still abgeschnitten, bevor TRIM greift: Aus ' FALSE' (6 Zeichen) macht ein nvarchar(5)-Parameter ein ' FALS', das die CASE-Liste nicht mehr erkennt. Für eine konkrete Quelle richtet sich die Länge nach dem Quellschema.

Rückgabe

Gibt den umgewandelten Wert als bit zurück, wenn die Umwandlung erfolgreich ist. Kann der Eingangswert nicht zugeordnet werden, wird NULL zurückgegeben.

Unterstützte Eingangswerte

Eingangswert → 1Eingangswert → 0
JN
JANEIN
YNO
YES
TRUEFALSE
ONOFF
1-10
x-

Für die hier verwendeten ASCII-Notationen arbeitet die Funktion case-insensitiv: UPPER normalisiert den Eingangswert vor dem CASE-Vergleich, jaJa und JA landen also auf demselben Mapping. Bei Notationen außerhalb des ASCII-Bereichs hängt UPPER von der Kollation ab: Unter türkischer Kollation wird aus 'nein' etwa 'NEİN' mit İ-Punkt, das die Liste nicht mehr trifft. Die Tabelle ist die Positivliste der Funktion — vollständig ist sie immer nur gegenüber einer konkreten Quelle, eine allgemeingültige Liste aller Ja/Nein-Notationen gibt es nicht. Die Funktion ist deshalb als Ausgangs-Skelett zu verstehen. Pro Datenquelle wird sie an die tatsächlich gelieferten Notationen angepasst (z. B. 'wahr'/'falsch''sí'/'no').

Code

Vollständige Funktions-Definition:

  1: CREATE FUNCTION [dbo].[fn_convert_bit] (@p_value AS nvarchar(50))
  2: RETURNS bit
  3: AS
  4: BEGIN
  5:    DECLARE @return_value AS bit;
  6: 
  7:    SET @p_value      = UPPER(TRIM(@p_value));
  8:    SET @return_value = CASE @p_value
  9:                             WHEN N'J'     THEN 1
 10:                             WHEN N'JA'    THEN 1
 11:                             WHEN N'Y'     THEN 1
 12:                             WHEN N'YES'   THEN 1
 13:                             WHEN N'N'     THEN 0
 14:                             WHEN N'NEIN'  THEN 0
 15:                             WHEN N'NO'    THEN 0
 16:                             WHEN N'TRUE'  THEN 1
 17:                             WHEN N'FALSE' THEN 0
 18:                             WHEN N'ON'    THEN 1
 19:                             WHEN N'OFF'   THEN 0
 20:                             WHEN N'X'     THEN 1  -- quellspezifisch
 21:                             WHEN N'-1'    THEN 1
 22:                             WHEN N'1'     THEN 1
 23:                             WHEN N'0'     THEN 0
 24:                             WHEN N'-'     THEN 0  -- quellspezifisch
 25:                             ELSE NULL
 26:                        END;
 27: 
 28:    RETURN @return_value;
 29: END;

Demo-Aufrufe

16 Aufrufe, mit dem jeweils erwarteten Ergebnis als Kommentar:

  1: SELECT [dbo].[fn_convert_bit](N'1');      -- 1
  2: SELECT [dbo].[fn_convert_bit](N'0');      -- 0
  3: SELECT [dbo].[fn_convert_bit](N'-1');     -- 1
  4: SELECT [dbo].[fn_convert_bit](N'J');      -- 1
  5: SELECT [dbo].[fn_convert_bit](N'ja');     -- 1    (case-insensitiv über UPPER im Funktions-Body)
  6: SELECT [dbo].[fn_convert_bit](N'nein');   -- 0
  7: SELECT [dbo].[fn_convert_bit](N'x');      -- 1    (Legacy-Marker: gesetzt)
  8: SELECT [dbo].[fn_convert_bit](N'-');      -- 0    (Legacy-Marker: nicht gesetzt)
  9: SELECT [dbo].[fn_convert_bit](N'true');   -- 1
 10: SELECT [dbo].[fn_convert_bit](N'false');  -- 0
 11: SELECT [dbo].[fn_convert_bit](N'ON');     -- 1
 12: SELECT [dbo].[fn_convert_bit](N'OFF');    -- 0
 13: SELECT [dbo].[fn_convert_bit](N' ja ');   -- 1    (Leerzeichen-Padding, TRIM greift)
 14: SELECT [dbo].[fn_convert_bit](N'');       -- NULL (leere Zeichenfolge; direktes TRY_CONVERT ergäbe 0)
 15: SELECT [dbo].[fn_convert_bit](N'?');      -- NULL (unbekannter Eingangswert)
 16: SELECT [dbo].[fn_convert_bit](NULL);      -- NULL (NULL-Input bleibt NULL)

Postgres-Brücke

In Postgres ist die Lage komfortabler: boolean ist ein nativer Datentyp, ein bit-Ersatz ist nicht nötig. Der Direkt-Cast s::boolean versteht ohne Zusatz-Aufwand eine deutlich umfangreichere Yes/No-Liste als SQL Servers bit-Parser:

  1: SELECT 't'::boolean;         -- true
  2: SELECT 'true'::boolean;      -- true
  3: SELECT 'TRUE'::boolean;      -- true   (case-insensitiv)
  4: SELECT '  true  '::boolean;  -- true   (umgebender Leerraum wird entfernt)
  5: SELECT 'tr'::boolean;        -- true   (eindeutiges Präfix von true)
  6: SELECT 'y'::boolean;         -- true
  7: SELECT 'yes'::boolean;       -- true
  8: SELECT 'on'::boolean;        -- true
  9: SELECT '1'::boolean;         -- true
 10: 
 11: SELECT 'f'::boolean;         -- false
 12: SELECT 'n'::boolean;         -- false
 13: SELECT 'no'::boolean;        -- false
 14: SELECT 'off'::boolean;       -- false
 15: SELECT '0'::boolean;         -- false

Drei Unterschiede gegenüber SQL Server:

  • Breitere Eingabeliste: 't'/'y'/'yes'/'on'/'1' und ihre false-Pendants, case-insensitiv und mit Leerraum-Toleranz. Sogar eindeutige Präfixe akzeptiert der Parser ('tr''ye'). Nur das mehrdeutige 'o' lehnt er ab, weil es 'on' wie 'off' sein könnte.
  • Nur '1' und '0' als Ziffern: Die T-SQL-Regel „alles außer 0 ergibt 1“ gibt es nicht — '-1'::boolean und '42'::boolean werfen eine Exception. Numerische Wahrheitswerte aus einer SQL-Server-Quelle müssen in Postgres vorab auf '0'/'1' normalisiert werden.
  • Exception statt stiller Werte: Ungültige Eingaben wie 'J' und auch die leere Zeichenfolge brechen mit invalid input syntax for type boolean ab (SQLSTATE 22P02, Fehlerklasse invalid_text_representation). Ein eingebautes Try-Pendant existiert nicht, auch in Postgres 18 nicht.

Das NULL-statt-Exception-Verhalten plus die deutschen Notationen liefert ein PL/pgSQL-Wrapper mit derselben CASE-Zuordnung wie die T-SQL-Funktion:

  1: CREATE OR REPLACE FUNCTION fn_try_cast_boolean
  2: (
  3:     IN    p_input              text
  4: )
  5: RETURNS boolean
  6: LANGUAGE plpgsql
  7: IMMUTABLE
  8: AS $function$
  9: DECLARE
 10:    l_normalized              text;
 11: BEGIN
 12: 
 13:    IF p_input IS NULL OR TRIM(p_input) = '' THEN
 14:       RETURN NULL;
 15:    END IF;
 16: 
 17:    l_normalized := UPPER(TRIM(p_input));
 18: 
 19:    RETURN CASE l_normalized
 20:              WHEN 'J'     THEN true
 21:              WHEN 'JA'    THEN true
 22:              WHEN 'Y'     THEN true
 23:              WHEN 'YES'   THEN true
 24:              WHEN 'TRUE'  THEN true
 25:              WHEN 'T'     THEN true
 26:              WHEN 'ON'    THEN true
 27:              WHEN '1'     THEN true
 28:              WHEN '-1'    THEN true
 29:              WHEN 'X'     THEN true   -- quellspezifisch
 30:              WHEN 'N'     THEN false
 31:              WHEN 'NEIN'  THEN false
 32:              WHEN 'NO'    THEN false
 33:              WHEN 'FALSE' THEN false
 34:              WHEN 'F'     THEN false
 35:              WHEN 'OFF'   THEN false
 36:              WHEN '0'     THEN false
 37:              WHEN '-'     THEN false  -- quellspezifisch
 38:              ELSE NULL
 39:           END;
 40: 
 41: END;
 42: $function$;
 43: 
 44: SELECT fn_try_cast_boolean('J');       -- true
 45: SELECT fn_try_cast_boolean(' ja ');    -- true
 46: SELECT fn_try_cast_boolean('x');       -- true
 47: SELECT fn_try_cast_boolean('foo');     -- NULL
 48: SELECT fn_try_cast_boolean('');        -- NULL

Zwei Eigenheiten unterscheiden diesen Wrapper von seinen Geschwistern in den anderen Artikeln der Serie. Erstens braucht er keinen EXCEPTION-Block: Die CASE-Zuordnung wirft nie, unbekannte Werte laufen in ELSE NULL. Damit entfällt der Kosten-Aufschlag, den die Postgres-Doku für Blöcke mit Exception-Handler nennt. Zweitens erbt er nichts vom nativen Parser: Präfix-Formen wie 'tr' stehen nicht in der Liste und ergeben NULL — die CASE-Liste ist der Vertrag. Wer stattdessen beim nativen Cast bleiben will, prüft seit Postgres 16 mit pg_input_is_valid('J', 'boolean') set-basiert vor, ob ein Wert konvertierbar ist, ohne einen fehlschlagenden Cast auszulösen. Das ist ein Vorprüf-Pattern, kein Try-Cast: Geprüft und konvertiert wird in zwei getrennten Schritten gegen dieselbe Eingabe.

SQL ServerPostgres-PendantBemerkung
TRY_CONVERT(bit, '1')'1'::boolean oder fn_try_cast_boolean('1')Built-in identisch
TRY_CONVERT(bit, 'true')'true'::boolean oder fn_try_cast_boolean('true')Built-in identisch
TRY_CONVERT(bit, 'J') → NULL'J'::boolean → Exception, fn_try_cast_boolean('J') → trueWrapper deckt deutsche Notation
TRY_CONVERT(bit, '') → 0''::boolean → Exception, fn_try_cast_boolean('') → NULLdasselbe leere Feld, drei Ergebnisse
[dbo].[fn_convert_bit](N'YES')fn_try_cast_boolean('YES')funktionale Entsprechung

Zusammenfassung

  • Konvertierung nach bit ist zweistufig zu denken: erst prüfen, ob der eingebaute Konverter die Quelle abdeckt (Integer-Strings nach der „alles außer 0“-Regel plus 'true'/'false'), erst dann eine eigene Funktion einsetzen.
  • Der wichtigste Sonderfall des Built-ins: Leere Zeichenfolgen, Leerzeichen-Strings und einzelne Vorzeichen werden zu 0 konvertiert (gemessen, nicht dokumentiert) — ein leeres CSV-Feld wird so zum fachlichen „Nein“. Das sichere Pattern mappt die leere Zeichenfolge per CASE/TRIM vorab auf NULL.
  • [dbo].[fn_convert_bit] übernimmt Yes/No-Notationen ('J''JA''ON''x', …) über eine explizite Positivliste. Unbekannte Werte und die leere Zeichenfolge behandelt sie bewusst als NULL — das passt zur dreiwertigen Logik (0/1/NULL) des bit-Typs.
  • Postgres-Pendant: boolean nativ mit breiterer Eingabeliste ('t''yes''on', eindeutige Präfixe), aber Exception statt NULL bei ungültigem Input. fn_try_cast_boolean liefert das Try-Verhalten und die deutschen Notationen.

Für die Praxis als Entscheidungshilfe:

EingangslageEmpfohlener Weg
Quelle liefert nur '0'/'1'/'true'/'false'direkt TRY_CONVERT(bit, …)
Leere Zeichenfolgen möglich (CSV-Import)sicheres Pattern: CASE/TRIM mappt auf NULL, dann TRY_CONVERT
Typisierte Zahlen (0/1/-1, Berechnungs-Ergebnisse)direkt TRY_CONVERT(bit, …) — „alles außer 0 ergibt 1“
Yes/No-Notationen ('J''YES''ON''x')fn_convert_bit mit expliziter Positivliste
Gemischte oder unbekannte NotationenDISTINCT-Inventur der Quelle, Positivliste verbindlich festhalten
NULL unklarer Herkunft im Ziel-Feldset-basierte Gegenprobe (siehe FAQ)

FAQ

Warum gibt TRY_CONVERT(bit, 'J') NULL zurück?

Die bit-Konvertierung akzeptiert aus Text nur Integer-Strings ('0''1''-1', jede Zahl außer 0 ergibt 1) und die Literale 'true'/'false' (case-insensitiv). 'J' ist keines davon, also fällt der Aufruf auf NULL zurück. Im ETL-Pfad ist das gewollt — TRY_CONVERT löst keine Exception aus, sondern signalisiert „passt nicht“ über NULL. Für Yes/No-Notationen wie 'J' braucht es eine eigene Funktion mit CASE-Zuordnung (siehe [dbo].[fn_convert_bit]).

Warum wird TRY_CONVERT(bit, N'') zu 0 statt NULL?

SQL Server konvertiert die leere Zeichenfolge beim bit-Ziel zu 0 — die Konvertierung gilt als erfolgreich, es gibt weder Fehler noch NULL (gemessenes Verhalten auf SQL Server 2019 und 2022, von der Microsoft-Doku nicht festgelegt). Dasselbe gilt für Strings nur aus Leerzeichen und für ein einzelnes Vorzeichen ('-''+'), und dieselbe 0 liefert auch die Integer-Konvertierung TRY_CONVERT(int, N''). Im Import ist das selten gemeint, denn ein leeres Feld steht für „unbekannt“, nicht für „Nein“. Das sichere Pattern mappt die leere Zeichenfolge deshalb per CASE/TRIM vorab auf NULL, und fn_convert_bit hat genau dieses Verhalten eingebaut.

Was ist der Unterschied zwischen bit und boolean?

SQL Server besitzt keinen nativen BOOLEAN-Datentyp für Spalten oder Variablen. Boolesche Ausdrücke existieren nur intern in Prädikaten und Bedingungen (IS NULLEXISTSWHERE-Klauseln). Wer Wahrheitswerte in einer Tabelle ablegen will, nutzt bit als Ersatz. Postgres dagegen hat boolean als nativen Datentyp und akzeptiert beim Cast eine breitere Yes/No-Liste ('t'/'true'/'y'/'yes'/'on'/'1' und Pendants). Praktisch: In einer Cross-Engine-ETL-Pipeline ist bit (SQL Server) ↔ boolean (Postgres) das semantische Pendant — beide bilden zwei Wahrheitszustände plus NULL ab. Identisch sind die Typen damit nicht: Eingabegrammatik und Fehlerverhalten unterscheiden sich (siehe Postgres-Brücke).

Wie speichert man bit performant in einer breiten Tabelle?

SQL Server speichert mehrere bit-Spalten einer Zeile gemeinsam in einem Byte (bis zu 8 Spalten in 1 Byte). Die Gruppierung übernimmt die Storage-Engine intern — die Deklarationsreihenfolge im CREATE TABLE muss dafür nicht künstlich umgestellt werden. Bei einzelnen bit-Spalten ist der Speicher-Vorteil gegenüber tinyint in der Praxis meist gering. Der Unterschied ist primär semantisch: bit signalisiert „dreiwertige Logik mit 0/1/NULL“, tinyint signalisiert „kleine ganze Zahl 0–255“.

Was tun, wenn die Quelle gemischt 'YES''Y''1' und 'true' liefert?

Die fn_convert_bit-Funktion erweitern (alle vier Werte sind dort schon abgedeckt) — oder im SSIS-Derived-Column-Task vorverarbeiten, falls die Konvertierung in der Pipeline statt im T-SQL stattfindet. Wichtig ist die Vollständigkeit der Positivliste gegenüber der Quelle: Jede tatsächlich vorkommende Notation muss in der CASE-Liste stehen, sonst landen Datensätze auf NULL und der Import wirkt unvollständig. Im Zweifel pro Datenquelle eine Inventur der Distinct-Werte ziehen (SELECT DISTINCT col FROM stage o. ä.) und die Liste verbindlich festhalten.

Warum liefert TRY_CONVERT(bit, …) NULL, obwohl der Wert gültig aussieht?

Beim bit-Ziel gibt es drei NULL-Ursachen: Die Quelle hat NULL geliefert, die Notation ist dem Parser unbekannt ('J''YES''x'), oder unsichtbare Zeichen stören — schon ein Tab statt eines Leerzeichens reicht, denn der Parser toleriert nur das normale Leerzeichen. Die set-basierte Gegenprobe SELECT DISTINCT spalte FROM quelle WHERE spalte IS NOT NULL AND TRY_CONVERT(bit, spalte) IS NULL listet genau die Werte auf, die der Parser nicht kennt. Das ist zugleich die Basis für die nächste Erweiterung der CASE-Liste von fn_convert_bit.

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

Direkt: s::boolean (siehe Postgres-Brücke). Der Built-in deckt 't'/'true'/'y'/'yes'/'on'/'1' samt Pendants und eindeutigen Präfixen ab — mehr als SQL Servers bit-Parser. Bei ungültigem Input wirft er allerdings eine Exception, und ein eingebautes try_cast hat auch Postgres 18 nicht nachgeliefert. Das Try-Verhalten liefert der Wrapper fn_try_cast_boolean aus der Postgres-Brücke. Die Konvertierbarkeit lässt sich seit Postgres 16 zusätzlich mit pg_input_is_valid set-basiert vorprüfen.

Verwandte Artikel

ETL-Kontext:

TRY_CONVERT für andere Datentypen:

Grundlagen: