Drei Typen für Strings — und in PostgreSQL ist die Wahl eigentlich schon getroffen: text für fast alles. Es gibt keinen Performance-Unterschied zu varchar und keinen guten Grund mehr für char. Trotzdem hält sich aus MySQL- und Oracle-Welten das Reflex „nimm varchar(255)” — hier zeigen wir, warum das in Postgres unnötig ist.
Die drei Typen im Vergleich
| Typ | Längen-Check | Padding | Speicher | Wann nehmen? |
|---|---|---|---|---|
text | nein | nein | TOAST bei großen Werten | Default für (fast) alles |
varchar(n) | ja, max n Zeichen | nein | wie text + Length-Constraint | Wenn die Länge fachlich begrenzt sein muss |
varchar (ohne n) | nein | nein | wie text | identisch zu text |
char(n) | ja, exakt n Zeichen | füllt mit Spaces auf | n Zeichen fix | Sehr selten; Legacy |
In Postgres-Speicher ist text und varchar identisch — beide werden als variabel-langer String mit Header gespeichert. Es gibt keinen Performance-Vorteil, varchar(50) zu wählen.
Beispiele in der Praxis
CREATE TABLE users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE, -- E-Mail: keine sinnvolle Längen-Grenze
name text, -- Name: kann lang sein
country varchar(2) NOT NULL, -- ISO-3166-1 alpha-2 — exakt 2 Buchstaben
phone_e164 varchar(15) -- E.164 max 15 Stellen
);varchar(2) und varchar(15) haben einen sinnvollen Zweck: sie kodieren ein fachliches Limit ins Schema. Wer versucht, einen drei-Buchstaben-Country-Code zu inserten, bekommt einen Fehler. Das schützt vor falschen Eingaben.
text für email und name — hier wäre eine willkürliche Grenze schädlich. E-Mails laut RFC 5321 dürfen 254 Zeichen lang sein; chinesische Namen können in UTF-8 viele Bytes belegen. text schluckt alles.
Was passiert bei zu langen Werten?
myapp=> INSERT INTO users (email, country)
VALUES ('bob@example.com', 'GERMANY');
ERROR: value too long for type character varying(2)Postgres lehnt mit klarer Fehlermeldung ab. varchar schneidet nicht stillschweigend ab — anders als das Standard-Verhalten in MySQL ohne Strict-Mode.
Bei text gibt’s kein Längen-Limit:
myapp=> INSERT INTO articles (title)
VALUES (repeat('a', 1000000)); -- 1 Million Zeichen
INSERT 0 1Geht. Postgres lagert sehr lange Werte automatisch in die TOAST-Tabelle (transparent für die Anwendung).
char(n) — die Padding-Falle
char(n) ist die einzige Variante mit echten Eigenheiten:
myapp=> CREATE TABLE codes (
code char(5)
);
myapp=> INSERT INTO codes VALUES ('AB');
myapp=> SELECT code, length(code), '|' || code || '|' AS visualisiert
FROM codes;
code | length | visualisiert
-------+--------+--------------
AB | 5 | |AB |Postgres hat das 'AB' mit drei Spaces aufgefüllt. Das ist SQL-Standard und der Hauptgrund, warum char(n) heute selten die richtige Wahl ist:
- Vergleichsoperationen ignorieren das Padding (
'AB' = 'AB 'istTRUE), was verwirren kann. - Anwendungen müssen oft
trim(code)aufrufen, sonst landen Spaces im UI. - Der Speicher ist immer
nZeichen, auch wenn der Wert kürzer ist.
Ausnahme, in denen char(n) Sinn ergibt: sehr alte Mainframe-Migrationen oder fixe-Länge-Codes wie ISBN-10 (10 Stellen exakt). Für moderne Anwendungen: nicht.
Warum die alte varchar(255)-Tradition?
In MySQL hatten verschiedene varchar-Längen historisch unterschiedliche Speicher-Layouts. Bis zu 255 Zeichen reichte ein 1-Byte-Längen-Header; darüber 2 Bytes. Daraus entstand die Konvention, varchar(255) als „Standard-Maximum” zu wählen.
In Postgres existiert dieses Layout-Detail nicht. varchar(50) und varchar(50000) und text haben dieselbe interne Repräsentation. Die (255)-Konvention ist hier reine Folklore — kostet zwar nichts, signalisiert aber einen MySQL-Hintergrund.
Längen-Constraints später anpassen
varchar(n) lässt sich nachträglich vergrößern oder verkleinern:
-- Vergrößern: instant, kein Re-Write der Tabelle
ALTER TABLE users ALTER COLUMN phone_e164 TYPE varchar(20);
-- Verkleinern: erfordert Validierung — bei großen Tabellen langsam
ALTER TABLE users ALTER COLUMN phone_e164 TYPE varchar(10);
-- Schlägt fehl, wenn ein bestehender Wert länger istVergrößern (oder Wegfall der Grenze) ist seit PG 9.2 ohne Tabellen-Rewrite möglich. Verkleinerung ist nur möglich, wenn alle bestehenden Werte passen — sonst Error.
Umstieg von varchar(n) auf text:
ALTER TABLE users ALTER COLUMN email TYPE text;Sehr schnell, kein Datenverlust, kein Rewrite — die unterliegenden Daten sind ja schon im richtigen Format.
CHECK-Constraints als Alternative
Manchmal will man komplexere Beschränkungen als nur die Länge:
CREATE TABLE users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL CHECK (email ~ '^.+@.+\..+$'),
phone text CHECK (length(phone) BETWEEN 7 AND 20)
);CHECK-Constraints sind ausdrucksstärker als feste Längen — sie können Format-Validierung machen, Längen-Bereiche oder Mehrspalten-Bedingungen. Mehr im Kapitel Constraints.
FAQ
Soll ich varchar(255) oder text nehmen?
In Postgres: text. Es gibt keinen Performance- oder Speicher-Unterschied; varchar(n) macht nur dann Sinn, wenn n ein fachliches Limit ist (z. B. ISO-Country-Code = 2). „Standard 255” ist MySQL-Folklore und in Postgres unnötig.
Wie groß kann ein text-Wert werden?
Bis ca. 1 GB pro Wert. Werte über ~2 KB werden automatisch komprimiert und außerhalb der Tabelle in der TOAST-Tabelle gespeichert (transparent). Für die meisten Apps ist das mehr als genug — größere Blobs gehören in Object Storage, nicht in die DB.
Sind Strings Unicode?
Ja, sofern die Datenbank als UTF8 erstellt wurde (Default seit PG 8.0). Jeder String kann beliebige Unicode-Zeichen enthalten. length(s) zählt Zeichen (nicht Bytes); für Bytes nutze octet_length(s).
Wie schneide ich Strings ab?
substring(s, 1, 50) oder left(s, 50). Für „mit Ellipsis abschneiden”: CASE WHEN length(s) > 50 THEN substring(s, 1, 47) || '…' ELSE s END. Wer das oft braucht: in einer eigenen Funktion kapseln.
Vergleiche und Sortierung — sprachsensitiv?
Ja, abhängig von der Collation der Datenbank/Spalte. „ä” sortiert in de_DE.utf8 direkt nach „a”, in C (binary) ganz hinten. Bei wachsenden Text-Daten in mehreren Sprachen lohnt sich der Blick aufs Collation-Setup — siehe Artikel Encoding und Collation.
Pattern-Matching und Indexe.
LIKE 'foo%' (Prefix-Match) kann einen normalen B-tree-Index nutzen, wenn die Collation C/C.UTF-8 ist; bei sprachsensitiven Collations braucht’s text_pattern_ops. LIKE '%foo%' (Substring) braucht den pg_trgm-Index. Mehr im Indexe-Kapitel.
Weiterführende Ressourcen
Externe Quellen
- Character Types – PostgreSQL Documentation
- String Functions and Operators
- TOAST – PostgreSQL Documentation
- Don’t Do This: char(n) – PostgreSQL Wiki