boolean ist der einfachste Datentyp — und doch verbirgt er ein paar Eigenheiten. Drei Zustände (TRUE, FALSE, NULL), mehrere akzeptierte Eingabe-Schreibweisen und eine besondere Rolle in WHERE-Klauseln. Hier die wichtigsten Regeln, mit Beispielen.
TRUE, FALSE und NULL
Ein boolean kann drei Werte annehmen:
| Wert | Bedeutung |
|---|---|
TRUE | „wahr” / „ja” / „aktiv” |
FALSE | „falsch” / „nein” / „inaktiv” |
NULL | „unbekannt” / „nicht gesetzt” |
NULL ist nicht dasselbe wie FALSE. Wer is_active = NULL als „nicht aktiv” interpretiert, hat einen Bug — siehe NULL und Three-Valued Logic.
Akzeptierte Eingaben
Postgres akzeptiert eine ganze Reihe von Schreibweisen für TRUE und FALSE:
-- Diese Werte werden als TRUE interpretiert:
INSERT INTO settings (active) VALUES (TRUE);
INSERT INTO settings (active) VALUES (true);
INSERT INTO settings (active) VALUES ('t');
INSERT INTO settings (active) VALUES ('true');
INSERT INTO settings (active) VALUES ('y');
INSERT INTO settings (active) VALUES ('yes');
INSERT INTO settings (active) VALUES ('on');
INSERT INTO settings (active) VALUES ('1'); -- als String!
-- Diese als FALSE:
INSERT INTO settings (active) VALUES (FALSE);
INSERT INTO settings (active) VALUES ('f');
INSERT INTO settings (active) VALUES ('false');
INSERT INTO settings (active) VALUES ('n');
INSERT INTO settings (active) VALUES ('no');
INSERT INTO settings (active) VALUES ('off');
INSERT INTO settings (active) VALUES ('0'); -- als String!Aber nicht als nackte Zahl:
myapp=> INSERT INTO settings (active) VALUES (1);
ERROR: column "active" is of type boolean but expression is of type integer
LINE 1: INSERT INTO settings (active) VALUES (1);Postgres ist hier strenger als z. B. MySQL. 1 und 0 als Integer werden nicht automatisch zu Boolean umgewandelt — der String '1'/'0' schon. Wer das anders haben will, muss explizit casten: 1::boolean.
Default-Output
Im psql sieht das so aus:
myapp=> SELECT active FROM settings;
active
--------
t
f
(null)Postgres zeigt t für TRUE, f für FALSE, leer für NULL. Anwendungen sehen das anders — die meisten Treiber (node-postgres, psycopg, pgx, JDBC) liefern echte Boolean-Werte (true/false/null).
Boolean in WHERE-Klauseln
Eine boolean-Spalte kann direkt als Bedingung stehen — ohne = TRUE:
-- Statt:
SELECT * FROM users WHERE is_active = TRUE;
-- Lieber:
SELECT * FROM users WHERE is_active;
-- Und für FALSE:
SELECT * FROM users WHERE NOT is_active;Beide Formen sind funktional gleich, aber die direkte Schreibweise ist idiomatisch SQL. Achtung: Zeilen mit is_active = NULL werden in keinem dieser Fälle eingeschlossen — NULL ist weder TRUE noch FALSE.
Wer NULL als „aktiv per Default” behandeln will:
SELECT * FROM users WHERE is_active IS NOT FALSE;
-- Liefert sowohl TRUE als auch NULL.Typische Patterns
Soft-Delete Flag
CREATE TABLE users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE,
deleted_at timestamptz -- NULL = nicht gelöscht; Wert = wann gelöscht
);
-- Aktive User abfragen:
SELECT * FROM users WHERE deleted_at IS NULL;Hier wird ein timestamptz statt eines Booleans verwendet — du bekommst gratis dazu, wann etwas gelöscht wurde. Das ist meistens nützlicher als ein reines is_deleted boolean.
Echtes Boolean-Flag
CREATE TABLE feature_flags (
name text PRIMARY KEY,
is_enabled boolean NOT NULL DEFAULT FALSE,
description text
);
UPDATE feature_flags SET is_enabled = TRUE WHERE name = 'new_dashboard';NOT NULL DEFAULT FALSE macht klar: jeder Eintrag ist explizit aktiviert oder deaktiviert, kein „unbekannt”.
Mehrere Flags zusammenfassen
CREATE TABLE notifications (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id bigint NOT NULL,
via_email boolean NOT NULL DEFAULT TRUE,
via_sms boolean NOT NULL DEFAULT FALSE,
via_push boolean NOT NULL DEFAULT TRUE
);
-- User, die irgendeinen Kanal aktiv haben:
SELECT user_id FROM notifications
WHERE via_email OR via_sms OR via_push;
-- User, die ALLE Kanäle deaktiviert haben:
SELECT user_id FROM notifications
WHERE NOT via_email AND NOT via_sms AND NOT via_push;Aggregat-Funktionen mit Boolean
Postgres bringt zwei nützliche Aggregat-Funktionen für Booleans:
myapp=> SELECT
bool_and(via_email) AS alle_email, -- TRUE wenn ALLE TRUE
bool_or(via_sms) AS irgendwer_sms -- TRUE wenn IRGENDEINER TRUE
FROM notifications;
alle_email | irgendwer_sms
------------+---------------
t | tevery() ist ein Synonym für bool_and() (SQL-Standard).
count() mit Filter (statt CASE WHEN):
SELECT
count(*) AS total,
count(*) FILTER (WHERE is_active) AS active_count,
count(*) FILTER (WHERE NOT is_active) AS inactive_count
FROM users;Die FILTER-Klausel ist sauberer als der historische count(CASE WHEN is_active THEN 1 END)-Trick.
Interessantes
Boolean-Spalten sind 1 Byte groß.
Auch wenn ein Bit theoretisch reichen würde — Postgres speichert pro boolean-Spalte ein ganzes Byte (plus Header-Overhead bei NULLs). Bei dichten Tabellen mit vielen Flags überlegt man manchmal eine Bit-Maske als integer — meist ist das aber Microoptimierung; lieber lesbare Boolean-Spalten.
WHERE col vs. WHERE col = TRUE.
Funktional gleich, aber WHERE col ist idiomatisch SQL. Der = TRUE-Vergleich kommt oft aus Sprachen wie Java oder C, in denen jede Bedingung einen expliziten Vergleich braucht. In Postgres-SQL: weglassen.
boolean ist NICHT integer — anders als in MySQL.
Postgres lehnt INSERT … VALUES (1) in eine boolean-Spalte ab. MySQL akzeptiert das stillschweigend (und behandelt 0 = FALSE, alles andere = TRUE). Bei Migrationen aus MySQL: alle „0/1”-Spalten prüfen und ggf. casten oder die Anwendung anpassen.
Drei-Wege-Flags? Lieber Enum.
Wenn ein Status mehr als „ja/nein” abdecken soll (z. B. „pending/approved/rejected”), ist boolean mit NULL als drittem Wert eine schlechte Wahl — semantisch verwirrend. Stattdessen enum oder text mit Lookup-Tabelle nehmen. Mehr im Enum-Artikel.
Index auf boolean — selten sinnvoll.
Bei einer boolean-Spalte mit etwa 50/50-Verteilung bringt ein normaler Index nichts (Postgres macht trotzdem einen Sequential Scan). Sinnvoll: Partial Index auf den seltenen Wert: CREATE INDEX users_active ON users (id) WHERE is_active. So wird nur der kleine, häufig gefilterte Teil indiziert.
Weiterführende Ressourcen
Externe Quellen
- Boolean Type – PostgreSQL Documentation
- Boolean Operators – PostgreSQL Documentation
- Aggregate Functions – bool_and, bool_or, every
- FILTER-Klausel – Aggregate Expressions