Datenqualität beginnt bei der Typ-Konvertierung — und bei bit-Spalten zeigt sich das schon beim Eingangswert: Yes/No-Informationen liegen in Legacy-Quellen in unterschiedlichsten Notationen vor ('J', 'Y', 'ON', '1', 'x', '-', …). SQL Servers eingebautes TRY_CONVERT(bit, …) deckt nur den Ziffern-Standard plus 'true'/'false' ab — alles andere braucht eine eigene Konvertierungs-Funktion. Dieser Artikel beschreibt beides: was eingebaut funktioniert, wann eine fn_convert_bit-Funktion her muss und wie das Pendant in Postgres aussieht.
Kurzüberblick:
- Der Wertebereich eines
bitumfasst logisch genau ein Bit Information (0/1, plusNULLals 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 den Ziffern-Standard plus'true'/'false'— case-insensitiv und locale-unabhängig.- Eigene Funktion
[dbo].[fn_convert_bit]mappt Yes/No-Notationen ('J'/'YES'/'ON'/'x'/'-', …) auf 0/1 und liefertNULLfür unbekannte Werte. - Postgres-Brücke:
booleanals nativer Typ,s::booleanmit eingebauter Yes/No-Toleranz, plustry_cast_boolean-Wrapper für non-standard Notationen.
Voraussetzung: SQL Server 2017+ (die fn_convert_bit-Funktion unten verwendet TRIM, das erst ab 2017 verfügbar ist — auf 2012–2016 stattdessen LTRIM(RTRIM(...))). Beispiele sind ohne Sample-Datensatz lauffähig — reine Inline-Literale.
Inhalt
- Datentyp
bit— Wertebereich und Speicherbedarf TRY_CONVERT(bit, …)— was eingebaut funktioniert[dbo].[fn_convert_bit]— eigene Funktion für non-standard Notationen- Postgres-Brücke
- Zusammenfassung
- FAQ
- Verwandte Artikel
Datentyp bit — Wertebereich und Speicherbedarf
| Eigenschaft | Wert |
|---|---|
| Wertebereich | 0, 1 und NULL (dreiwertige Logik: 0, 1, „unbekannt“) |
| Speicherbedarf | Logisch ein Bit an Information pro Wert. SQL Server speichert BIT-Spalten platzsparend und packt bis zu 8 BIT-Spalten einer Zeile in 1 Byte. |
Vergleich mit 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 NULL, EXISTS, …). 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. Die in den folgenden Sektionen vorgestellten Konvertierungs-Wege mappen leere Zeichenfolgen daher konsequent auf NULL.
TRY_CONVERT(bit, …) — was eingebaut funktioniert
Bevor man eine eigene Funktion schreibt, lohnt sich der Blick auf das, was SQL Server schon liefert. TRY_CONVERT(bit, …) versteht eine schmale, aber präzise Liste von Inputs. Die Verarbeitung ist case-insensitiv und locale-unabhängig.
1: -- Standard-Inputs, die TRY_CONVERT(bit, ...) direkt versteht (Ergebnis: 0 oder 1):
2: SELECT TRY_CONVERT(bit, N'1'); -- 1
3: SELECT TRY_CONVERT(bit, N'0'); -- 0
4: SELECT TRY_CONVERT(bit, N'-1'); -- 1 (jeder von 0 verschiedene Integer-String > 1)
5: SELECT TRY_CONVERT(bit, N'true'); -- 1 (case-insensitiv)
6: SELECT TRY_CONVERT(bit, N'false'); -- 0
7: SELECT TRY_CONVERT(bit, 1); -- 1 (numerischer Input)
8: SELECT TRY_CONVERT(bit, 42); -- 1 (jeder von 0 verschiedene Integer > 1)
Der Built-in deckt also genau zwei Eingangs-Kategorien ab: Integer-Strings/Zahlen (mit der „alles außer 0 → 1″-Regel) und die Literale 'true'/'false' (case-insensitiv). Für alles andere fällt der Aufruf auf NULL zurück:
1: -- Non-Standard-Inputs, an denen TRY_CONVERT(bit, ...) scheitert (Ergebnis: NULL):
2: SELECT TRY_CONVERT(bit, N'J'); -- NULL
3: SELECT TRY_CONVERT(bit, N'YES'); -- NULL
4: SELECT TRY_CONVERT(bit, N'ON'); -- NULL
5: SELECT TRY_CONVERT(bit, N'x'); -- NULL
6: SELECT TRY_CONVERT(bit, N'-'); -- NULL
7: SELECT TRY_CONVERT(bit, N''); -- NULL (leere Zeichenfolge; getestet auf SQL Server 2019/2022)
8: SELECT TRY_CONVERT(bit, NULL); -- NULL
Bemerkenswert: TRY_CONVERT(bit, N'') liefert direkt NULL (getestet auf SQL Server 2019 und 2022). TRY_CONVERT(int, N'') konvertiert dagegen zum Wert 0 — siehe TRY_CONVERT // Konvertierung nach bigint, int, smallint, tinyint. Im bit-Pfad braucht es daher kein zusätzliches CASE/TRIM-Pattern, um die leere Zeichenfolge abzufangen. Wer auf älteren Versionen unterwegs ist, sollte das Verhalten gegen die konkrete Edition testen — String-Konvertierungen in SQL Server haben historisch versionsabhängige Eigenheiten.
Wenn die Quelle nur '0'/'1'/'true'/'false' liefert, ist TRY_CONVERT ausreichend. Für alles andere — 'J', 'YES', 'ON', 'x', '-', … — kommt die nächste Sektion ins Spiel.
[dbo].[fn_convert_bit] — eigene Funktion für non-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', plus historische Markierungen wie 'x' (gesetzt) und '-' (nicht gesetzt). Die folgende Funktion mappt diese Notationen 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 case-insensitiv behandelt (UPPER im Funktions-Body). Leerzeichen am Anfang und Ende werden ignoriert (TRIM).
Syntax
1: [dbo].[fn_convert_bit](@p_value AS nvarchar(50))
Argumente
p_value— der umzuwandelnde Eingangswert. Parameter-Typ istnvarchar(50)(statt der knapperennvarchar(5)aus der Erstfassung) — das gibt Whitespace-Padding aus der Quelle Spielraum, bevor dasTRIMgreift.
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 → 1 | Eingangswert → 0 |
|---|---|
J | N |
JA | NEIN |
Y | NO |
YES | — |
TRUE | FALSE |
ON | OFF |
1, -1 | 0 |
| — | - |
Werte sind case-insensitiv. Im Funktions-Body normalisiert UPPER den Eingangswert vor dem CASE-Vergleich. Die Funktion ist 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'-1' THEN 1
21: WHEN N'1' THEN 1
22: WHEN N'0' THEN 0
23: WHEN N'-' THEN 0
24: ELSE NULL
25: END;
26:
27: RETURN @return_value;
28: END;
Demo-Aufrufe
15 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'J'); -- 1
4: SELECT [dbo].[fn_convert_bit](N'ja'); -- 1 (case-insensitiv über UPPER im Funktions-Body)
5: SELECT [dbo].[fn_convert_bit](N'N'); -- 0
6: SELECT [dbo].[fn_convert_bit](N'nein'); -- 0
7: SELECT [dbo].[fn_convert_bit](N' '); -- NULL (nach TRIM eine leere Zeichenfolge, nicht in CASE-Liste)
8: SELECT [dbo].[fn_convert_bit](N'X'); -- NULL (unbekannter Eingangswert)
9: SELECT [dbo].[fn_convert_bit](NULL); -- NULL (NULL-Input bleibt NULL)
10: SELECT [dbo].[fn_convert_bit](N'true'); -- 1
11: SELECT [dbo].[fn_convert_bit](N'false'); -- 0
12: SELECT [dbo].[fn_convert_bit](N'ON'); -- 1
13: SELECT [dbo].[fn_convert_bit](N'OFF'); -- 0
14: SELECT [dbo].[fn_convert_bit](N'-'); -- 0
15: SELECT [dbo].[fn_convert_bit](N'-1'); -- 1
Postgres-Brücke
In Postgres ist die Lage komfortabler: boolean ist ein nativer Datentyp, und der Direkt-Cast s::boolean versteht ohne Zusatz-Aufwand eine umfangreichere Yes/No-Liste als SQL Servers TRY_CONVERT(bit, …).
1: SELECT 't'::boolean; -- true
2: SELECT 'true'::boolean; -- true
3: SELECT 'y'::boolean; -- true
4: SELECT 'yes'::boolean; -- true
5: SELECT 'on'::boolean; -- true
6: SELECT '1'::boolean; -- true
7:
8: SELECT 'f'::boolean; -- false
9: SELECT 'n'::boolean; -- false
10: SELECT 'no'::boolean; -- false
11: SELECT 'off'::boolean; -- false
Allerdings: s::boolean wirft eine Exception bei ungültigen Inputs ('J'::boolean → ERROR: invalid input syntax for type boolean: "J"). Für non-standard Notationen — analog zur SQL-Server-Custom-Funktion — braucht es einen PL/pgSQL-Wrapper, der NULL statt Exception liefert und die deutschen 'J'/'JA'/'NEIN' plus 'x'/'-' mit abdeckt:
1: CREATE OR REPLACE FUNCTION try_cast_boolean (p_value text)
2: RETURNS boolean
3: LANGUAGE plpgsql
4: IMMUTABLE
5: AS $$
6: DECLARE
7: normalized text;
8: BEGIN
9: IF p_value IS NULL THEN
10: RETURN NULL;
11: END IF;
12: normalized := UPPER(TRIM(p_value));
13: IF normalized = '' THEN
14: RETURN NULL;
15: END IF;
16: RETURN CASE normalized
17: WHEN 'J' THEN true
18: WHEN 'JA' THEN true
19: WHEN 'Y' THEN true
20: WHEN 'YES' THEN true
21: WHEN 'TRUE' THEN true
22: WHEN 'T' THEN true
23: WHEN 'ON' THEN true
24: WHEN '1' THEN true
25: WHEN '-1' THEN true
26: WHEN 'N' THEN false
27: WHEN 'NEIN' THEN false
28: WHEN 'NO' THEN false
29: WHEN 'FALSE' THEN false
30: WHEN 'F' THEN false
31: WHEN 'OFF' THEN false
32: WHEN '0' THEN false
33: WHEN '-' THEN false
34: ELSE NULL
35: END;
36: END;
37: $$;
| SQL Server | Postgres-Pendant | Bemerkung |
|---|---|---|
TRY_CONVERT(bit, '1') | '1'::boolean oder try_cast_boolean('1') | Built-in identisch |
TRY_CONVERT(bit, 'true') | 'true'::boolean oder try_cast_boolean('true') | Built-in identisch |
TRY_CONVERT(bit, 'J') → NULL | 'J'::boolean → Exception; try_cast_boolean('J') → true | Wrapper deckt deutsche Notation |
[dbo].[fn_convert_bit](N'YES') | try_cast_boolean('YES') | volle Parallelität |
Zusammenfassung
Konvertierung nach bit ist zweistufig zu denken. Erst prüfen, ob TRY_CONVERT(bit, …) die Quelle abdeckt: der eingebaute Konverter versteht den Ziffern-Standard plus 'true'/'false', case-insensitiv und locale-unabhängig. Erst dann, wenn die Quelle Nicht-Standard-Notationen liefert ('J', 'YES', 'ON', 'x', …), eine eigene Funktion [dbo].[fn_convert_bit] mit CASE-Zuordnung einsetzen. Beide Wege liefern NULL für unbekannte Werte — das passt zur dreiwertigen Logik (0/1/NULL) des bit-Typs.
Take-Away:
TRY_CONVERT(bit, …)zuerst. Der eingebaute Konverter deckt mehr ab, als der Erst-Reflex „ich brauche eine eigene Funktion“ vermuten lässt.fn_convert_bitnur für Nicht-Standard-Notationen. Pro Datenquelle wird dieCASE-Liste an die tatsächlich gelieferten Werte angepasst.@p_value AS nvarchar(50)als Default-Parameter-Typ. Whitespace-Padding aus der Quelle bricht nicht vor demTRIM.- Postgres-Pendant über
s::boolean(Built-in) undtry_cast_boolean(Wrapper für Nicht-Standard-Notationen). Vorteil gegenüber SQL Server:booleanist nativ, und der Built-in akzeptiert von Haus aus eine breitere Yes/No-Liste alsTRY_CONVERT(bit, …).
FAQ
Warum gibt TRY_CONVERT(bit, 'J') NULL zurück?
Der bit-Parser kennt nur Integer-Strings ('0', '1', '-1', jede Zahl außer 0 → 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-Mapping (siehe [dbo].[fn_convert_bit]).
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 NULL, EXISTS, WHERE-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, der Wertebereich ist identisch (0/1/NULL bzw. true/false/NULL).
Wie speichere ich 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 nur die Erschöpfung der Liste: jede tatsächlich in der Quelle 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.
Postgres-Pendant für TRY_CONVERT(bit, …)?
Direkt: s::boolean (siehe Postgres-Brücke). Der Built-in deckt schon 't'/'true'/'y'/'yes'/'on'/'1' und Pendants ab — mehr als SQL Servers bit-Cast. Aber: er wirft Exception statt NULL bei ungültigem Input. Für äquivalente Fehlertoleranz braucht es einen try_cast_boolean-PL/pgSQL-Wrapper analog zu TRY_CONVERT // Konvertierung nach bigint, int, smallint, tinyint. Ab Postgres 18 (Ende 2025) ist try_cast eingebaut.
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 bigint, int, smallint, tinyint
- TRY_CONVERT // Konvertierung nach money, smallmoney
- TRY_CONVERT // Konvertierung nach float, real
- TRY_CONVERT // Konvertierung nach date, datetime, datetime2, time