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

TypLängen-CheckPaddingSpeicherWann nehmen?
textneinneinTOAST bei großen WertenDefault für (fast) alles
varchar(n)ja, max n Zeichenneinwie text + Length-ConstraintWenn die Länge fachlich begrenzt sein muss
varchar (ohne n)neinneinwie textidentisch zu text
char(n)ja, exakt n Zeichenfüllt mit Spaces aufn Zeichen fixSehr 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

SQL Eine typische Tabelle
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?

SQL varchar mit Längen-Check
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:

SQL
myapp=> INSERT INTO articles (title)
        VALUES (repeat('a', 1000000));   -- 1 Million Zeichen
INSERT 0 1

Geht. 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:

SQL
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 ' ist TRUE), was verwirren kann.
  • Anwendungen müssen oft trim(code) aufrufen, sonst landen Spaces im UI.
  • Der Speicher ist immer n Zeichen, 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:

SQL ALTER COLUMN TYPE
-- 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 ist

Vergröß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:

SQL
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:

SQL CHECK statt varchar(n)
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

/ Weiter

Zurück zu Datentypen

Zur Übersicht