PostgreSQL bietet mit den Datentypen json und jsonb starke Unterstützung zum Speichern und Abfragen von JSON-Daten. Besonders nützlich ist der Typ jsonb, da er die Daten in einem Binärformat speichert und so effizienteres Indexieren und Abfragen ermöglicht als der textbasierte Typ json.
In diesem Tutorial sehen wir uns die gängigsten JSON-Operationen in PostgreSQL an – mit praktischen Beispielen, die du direkt in deinen Anwendungen nutzen kannst.
JSON-Daten anlegen
Legen wir zuerst eine Tabelle mit einer jsonb-Spalte an und fügen ein paar Beispieldaten ein:
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
info JSONB
);
-- Insert a customer with basic information
INSERT INTO customers (info) VALUES (
'{"name": "Alice Smith", "email": "alice@example.com", "age": 32, "addresses": [
{"type": "home", "city": "San Francisco", "state": "CA"},
{"type": "work", "city": "Oakland", "state": "CA"}
]}'
);
-- Insert another customer
INSERT INTO customers (info) VALUES (
'{"name": "Bob Johnson", "email": "bob@example.com", "age": 45, "addresses": [
{"type": "home", "city": "Seattle", "state": "WA"}
], "orders": [101, 102, 103]}'
);
Grundlegende JSON-Operationen
Das gesamte JSON-Dokument abrufen
SELECT info FROM customers WHERE id = 1;
Ergebnis:
{
"age": 32,
"name": "Alice Smith",
"email": "alice@example.com",
"addresses": [
{"city": "San Francisco", "type": "home", "state": "CA"},
{"city": "Oakland", "type": "work", "state": "CA"}
]
}
Werte aus JSON extrahieren
PostgreSQL bietet mehrere Operatoren, um Werte aus JSON zu extrahieren:
Ein bestimmtes Feld mit dem Operator -> extrahieren (liefert JSON)
-- Get the name field as JSON
SELECT info -> 'name' FROM customers;
Ergebnis:
"Alice Smith"
"Bob Johnson"
Ein bestimmtes Feld mit dem Operator -» extrahieren (liefert Text)
-- Get the name field as text
SELECT info ->> 'name' AS customer_name FROM customers;
Ergebnis:
customer_name
----------------
Alice Smith
Bob Johnson
Auf verschachtelte Objekte zugreifen
-- Getting the first address (array element 0) city
SELECT info -> 'addresses' -> 0 ->> 'city' AS home_city FROM customers;
Ergebnis:
home_city
------------
San Francisco
Seattle
Den Operator #> für Pfadzugriffe nutzen
-- Using the path syntax to get the same value
SELECT info #> '{addresses,0,city}' FROM customers;
Ergebnis:
"San Francisco"
"Seattle"
Den Operator #» für Pfadzugriffe als Text nutzen
-- Using the path syntax to get the same value as text
SELECT info #>> '{addresses,0,city}' AS home_city FROM customers;
Ergebnis:
home_city
------------
San Francisco
Seattle
JSON-Daten ändern
PostgreSQL bietet mehrere Funktionen zum Ändern von JSON-Daten:
Ein Feld mit jsonb_set hinzufügen
-- Add a new field 'premium_customer' to a customer
UPDATE customers
SET info = jsonb_set(info, '{premium_customer}', 'true')
WHERE id = 1;
Ein Feld mit dem Operator - entfernen
-- Remove the premium_customer field
UPDATE customers
SET info = info - 'premium_customer'
WHERE id = 1;
JSON-Objekte mit || zusammenführen
-- Add multiple fields by merging objects
UPDATE customers
SET info = info || '{"status": "active", "last_login": "2025-03-23"}'::jsonb
WHERE id = 1;
Ein verschachteltes Feld aktualisieren
-- Update a nested field (home address state)
UPDATE customers
SET info = jsonb_set(
info,
'{addresses,0,state}',
'"NY"'::jsonb
)
WHERE id = 1;
JSON-Arrays abfragen
Die Arbeit mit JSON-Arrays ist eine häufige Aufgabe:
Array-Länge ermitteln
-- Get the number of addresses for each customer
SELECT
info ->> 'name' AS customer_name,
jsonb_array_length(info -> 'addresses') AS address_count
FROM customers;
Ergebnis:
customer_name | address_count
---------------+--------------
Alice Smith | 2
Bob Johnson | 1
Bestimmte Array-Elemente extrahieren
-- Get the second address (index 1) for Alice
SELECT info -> 'addresses' -> 1 FROM customers WHERE id = 1;
Ergebnis:
{"city": "Oakland", "type": "work", "state": "CA"}
Arrays entpacken, um einzelne Elemente abzufragen
-- Unnest the addresses array to query individual addresses
SELECT
c.id,
c.info ->> 'name' AS customer_name,
jsonb_array_elements(c.info -> 'addresses') AS address
FROM customers c;
Ergebnis:
id | customer_name | address
---+---------------+------------------------------------------------------
1 | Alice Smith | {"city": "San Francisco", "type": "home", "state": "NY"}
1 | Alice Smith | {"city": "Oakland", "type": "work", "state": "CA"}
2 | Bob Johnson | {"city": "Seattle", "type": "home", "state": "WA"}
Mit entpackten Arrays filtern
-- Find customers with addresses in CA
SELECT DISTINCT
c.id,
c.info ->> 'name' AS customer_name
FROM customers c,
jsonb_array_elements(c.info -> 'addresses') AS addr
WHERE addr ->> 'state' = 'CA';
Ergebnis:
id | customer_name
---+--------------
1 | Alice Smith
Auf Schlüssel und Werte prüfen
Prüfen, ob ein Schlüssel existiert
-- Find customers who have the 'orders' key
SELECT id, info ->> 'name' AS customer_name
FROM customers
WHERE info ? 'orders';
Ergebnis:
id | customer_name
---+--------------
2 | Bob Johnson
Auf Werte in Arrays prüfen
-- Find customers with a specific order number
SELECT id, info ->> 'name' AS customer_name
FROM customers
WHERE info -> 'orders' ? '101';
Ergebnis:
id | customer_name
---+--------------
2 | Bob Johnson
Prüfen, ob ein Array-Element passt
-- Find customers who have an address in Oakland (using path operators)
SELECT id, info ->> 'name' AS customer_name
FROM customers
WHERE info @> '{"addresses": [{"city": "Oakland"}]}';
Ergebnis:
id | customer_name
---+--------------
1 | Alice Smith
JSON-Daten indexieren
Für Produktivdatenbanken mit großen JSON-Datenmengen ist eine passende Indexierung unverzichtbar:
Einen GIN-Index für Containment-Abfragen anlegen
-- Create a GIN index for faster containment operations
CREATE INDEX idx_customers_info ON customers USING GIN (info);
Einen gezielten Index für ein JSON-Feld anlegen
-- Create an index on the 'email' field for fast lookups
CREATE INDEX idx_customers_email ON customers ((info ->> 'email'));
Indexierte Felder in Abfragen nutzen
-- This query can use the index we created above
SELECT id, info ->> 'name' AS customer_name
FROM customers
WHERE info ->> 'email' = 'alice@example.com';
Zwischen JSON und Tabellen konvertieren
JSON in Zeilen umwandeln (jsonb_to_record)
-- Extract JSON fields into a tabular structure
SELECT
id,
rec.name,
rec.email,
rec.age
FROM
customers,
jsonb_to_record(info) AS rec(
name text,
email text,
age int
);
Ergebnis:
id | name | email | age
---+--------------+-------------------+-----
1 | Alice Smith | alice@example.com | 32
2 | Bob Johnson | bob@example.com | 45
Verschachtelte Arrays in Tabellen umwandeln (jsonb_to_recordset)
-- Extract addresses from all customers into a table
SELECT
c.id,
c.info ->> 'name' AS customer_name,
addr.type AS address_type,
addr.city,
addr.state
FROM
customers c,
jsonb_to_recordset(c.info -> 'addresses') AS addr(
type text,
city text,
state text
);
Ergebnis:
id | customer_name | address_type | city | state
---+---------------+--------------+---------------+------
1 | Alice Smith | home | San Francisco | NY
1 | Alice Smith | work | Oakland | CA
2 | Bob Johnson | home | Seattle | WA
Abfrageergebnisse in JSON umwandeln
-- Aggregate customer orders into a JSON array
SELECT
c.id,
c.info ->> 'name' AS customer_name,
json_agg(o.order_id) AS orders
FROM
customers c
CROSS JOIN LATERAL (
SELECT jsonb_array_elements_text(c.info -> 'orders') AS order_id
) o
GROUP BY c.id, c.info ->> 'name';
Ergebnis:
id | customer_name | orders
---+---------------+----------
2 | Bob Johnson | ["101", "102", "103"]
Hinweise zur Performance
Wenn du mit JSON in PostgreSQL arbeitest, solltest du diese Performance-Aspekte im Blick behalten:
-
Nutze
jsonbstattjson: Der Typjsonbbietet bei den meisten Operationen eine bessere Performance. -
Gezielt indexieren: Nutze GIN-Indizes für Containment-Abfragen, lege aber für häufig abgefragte Felder eigene Indizes an.
-
Denormalisierung gut abwägen: JSON erlaubt dir zwar, dein Datenmodell zu denormalisieren, doch zu tiefe Verschachtelung kann die Abfrage-Performance beeinträchtigen.
-
Abfragemuster im Blick behalten: Komplexe JSON-Pfadausdrücke oder häufiges Entpacken großer Arrays können teure Operationen sein.
-
Array-Größe begrenzen: Sehr große JSON-Arrays können beim Entpacken zu Performance-Problemen führen.
-
Projektion nutzen: Extrahiere nur die Felder, die du brauchst, statt das gesamte JSON-Dokument zurückzugeben.
Fazit
Dank seiner JSON-Funktionen ist PostgreSQL eine hervorragende Wahl für Anwendungen, die halbstrukturierte Daten speichern und abfragen müssen. Mit den Operatoren und Funktionen aus diesem Tutorial kannst du effektiv mit JSON-Daten arbeiten und gleichzeitig die Leistung und Zuverlässigkeit einer ausgereiften relationalen Datenbank nutzen.
Für komplexe Datenmodelle lohnt sich ein hybrider Ansatz: Nutze strukturierte Tabellen für feste Schemas und häufig abgefragte Daten und JSON für flexible, schemalose Daten oder verschachtelte Strukturen, die sich relational nur umständlich modellieren lassen.
Willst du die Beispiele selbst ausprobieren? Öffne Beekeeper Studio und entdecke noch heute die JSON-Funktionen von PostgreSQL.
Beekeeper Studio ist eine kostenlose & quelloffene Datenbank-GUI
Das beste SQL-Abfrage- & Editor-Tool, das ich je benutzt habe. Es bietet alles, was ich zum Verwalten meiner Datenbank brauche. - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studio ist schnell, intuitiv und einfach zu bedienen. Beekeeper unterstützt jede Menge Datenbanken und läuft hervorragend auf Windows, Mac und Linux.
Was Nutzer über Beekeeper Studio sagen
„Beekeeper Studio hat meinen alten SQL-Workflow komplett ersetzt. Es ist schnell, intuitiv und macht die Arbeit mit Datenbanken wieder zum Vergnügen.“
„Ich habe viele Datenbank-GUIs ausprobiert, aber Beekeeper trifft die perfekte Balance zwischen Funktionsumfang und Einfachheit. Es funktioniert einfach.“