SQL vs NoSQL: Relationale vs Dokumentenorientierte Datenbanken & Use Cases
Dieser Beitrag ist ein umfassender Vergleich von SQL und NoSQL Datenbanken – mit Fokus auf relationale (MySQL, PostgreSQL) und dokumentenorientierte (MongoDB) Datenbanken und ihren Use Cases.
In a Nutshell
SQL-Datenbanken sind relational mit festem Schema, NoSQL-Datenbanken sind flexibel mit dynamischem Schema. Die Wahl hängt von Datenstruktur, Skalierbarkeit und Anwendungsfällen ab.
Kompakte Fachbeschreibung
SQL (Structured Query Language) und NoSQL (Not Only SQL) repräsentieren zwei grundlegend verschiedene Ansätze zur Datenspeicherung.
SQL-Datenbanken (Relational)
- Schema: Festes, vordefiniertes Schema
- Struktur: Tabellen mit Zeilen und Spalten
- ACID: Atomicity, Consistency, Isolation, Durability
- Beispiele: MySQL, PostgreSQL, Oracle, SQL Server
- Abfragen: SQL mit JOINS, Aggregationen, Subqueries
NoSQL-Datenbanken (Dokumentenorientiert)
- Schema: Flexibles, dynamisches Schema
- Struktur: JSON/BSON Dokumente
- BASE: Basically Available, Soft state, Eventually consistent
- Beispiele: MongoDB, CouchDB, DynamoDB
- Abfragen: Query Language, Aggregation Pipelines
Hauptunterschiede:
- Datenmodell: Relational vs Dokumentenorientiert
- Skalierbarkeit: Vertikal vs Horizontal
- Konsistenz: Stark vs Eventual
- Flexibilität: Festes vs Dynamisches Schema
Prüfungsrelevante Stichpunkte
- SQL: Relationale Datenbanken mit festem Schema, ACID-Eigenschaften
- NoSQL: Dokumentenorientierte Datenbanken mit flexiblem Schema
- MySQL/PostgreSQL: Populäre relationale Datenbanken
- MongoDB: Populäre NoSQL-Dokumentendatenbank
- Use Cases: SQL für strukturierte Daten, NoSQL für flexible Daten
- Skalierbarkeit: SQL vertikal, NoSQL horizontal
- Konsistenz: SQL stark, NoSQL eventual
- IHK-relevant: Wichtig für Datenbankdesign und -auswahl
Kernkomponenten
- SQL-Datenbanken: Tabellen, Schema, JOINS, ACID
- NoSQL-Datenbanken: Dokumente, Collections, flexible Schema
- Datenmodell: Relational vs Dokumentenorientiert
- Abfragesprachen: SQL vs NoSQL Query Language
- Skalierbarkeit: Vertikal vs Horizontal
- Konsistenzmodelle: ACID vs BASE
- Use Cases: Strukturierte vs Flexible Daten
- Performance: Lese/Schreib-Optimierung
Praxisbeispiele
1. SQL-Datenbank (MySQL/PostgreSQL) Beispiel
-- Schema Definition
CREATE TABLE kunden (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
geburtsdatum DATE,
adresse_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (adresse_id) REFERENCES adressen(id)
);
CREATE TABLE adressen (
id INT PRIMARY KEY AUTO_INCREMENT,
strasse VARCHAR(255) NOT NULL,
stadt VARCHAR(100) NOT NULL,
plz VARCHAR(10) NOT NULL,
land VARCHAR(50) DEFAULT 'Deutschland'
);
CREATE TABLE bestellungen (
id INT PRIMARY KEY AUTO_INCREMENT,
kunden_id INT NOT NULL,
bestelldatum DATE NOT NULL,
gesamtbetrag DECIMAL(10,2) NOT NULL,
status ENUM('pending', 'processing', 'shipped', 'delivered') DEFAULT 'pending',
FOREIGN KEY (kunden_id) REFERENCES kunden(id) ON DELETE CASCADE
);
-- Datenmanipulation
INSERT INTO kunden (name, email, geburtsdatum, adresse_id)
VALUES ('Max Mustermann', 'max@example.com', '1990-05-15', 1);
INSERT INTO adressen (strasse, stadt, plz)
VALUES ('Hauptstraße 1', 'Berlin', '10115');
-- Komplexe Abfrage mit JOINS
SELECT
k.name,
k.email,
k.geburtsdatum,
a.strasse,
a.stadt,
COUNT(b.id) as anzahl_bestellungen,
SUM(b.gesamtbetrag) as umsatz
FROM kunden k
LEFT JOIN adressen a ON k.adresse_id = a.id
LEFT JOIN bestellungen b ON k.id = b.kunden_id
WHERE k.geburtsdatum BETWEEN '1980-01-01' AND '1995-12-31'
AND a.stadt = 'Berlin'
GROUP BY k.id, k.name, k.email, k.geburtsdatum, a.strasse, a.stadt
HAVING COUNT(b.id) > 0
ORDER BY umsatz DESC
LIMIT 10;
-- Transaktion mit ACID-Eigenschaften
BEGIN TRANSACTION;
UPDATE bestellungen
SET status = 'shipped',
bestelldatum = CURRENT_DATE
WHERE id = 123 AND status = 'pending';
INSERT INTO versand (bestellung_id, tracking_nummer, versanddatum)
VALUES (123, 'DE123456789', CURRENT_DATE);
COMMIT;
2. NoSQL-Datenbank (MongoDB) Beispiel
// MongoDB JavaScript Shell
// Collection und Dokumente (kein Schema erforderlich)
db.kunden.insertOne({
name: "Max Mustermann",
email: "max@example.com",
geburtsdatum: new Date("1990-05-15"),
adresse: {
strasse: "Hauptstraße 1",
stadt: "Berlin",
plz: "10115",
land: "Deutschland"
},
interessen: ["programmieren", "lesen", "reisen"],
premium: true,
created: new Date()
});
// Flexible Datenstruktur - verschiedene Felder möglich
db.kunden.insertMany([
{
name: "Alice Schmidt",
email: "alice@example.com",
alter: 28,
adresse: {
strasse: "Musterstraße 5",
stadt: "Hamburg",
plz: "20095"
},
interessen: ["design", "fotografie"],
social_media: {
twitter: "@alice_design",
instagram: "alice.photos"
}
},
{
name: "Bob Weber",
email: "bob@example.com",
alter: 35,
adresse: {
strasse: "Bahnhofstraße 10",
stadt: "München",
plz: "80331",
land: "Deutschland"
},
firma: {
name: "TechCorp",
position: "Senior Developer",
seit: new Date("2018-03-01")
},
interessen: ["programmierung", "klettern", "kochen"]
}
]);
// Bestellungen als separate Collection
db.bestellungen.insertOne({
kunden_id: ObjectId("..."), // Referenz auf Kunden
positionen: [
{
produkt: "Laptop",
anzahl: 1,
preis: 999.99
},
{
produkt: "Maus",
anzahl: 2,
preis: 29.99
}
],
gesamtbetrag: 1059.97,
status: "pending",
bestelldatum: new Date(),
zahlung: {
methode: "credit_card",
status: "paid",
transaktions_id: "txn_123456789"
}
});
// Komplexe Aggregation Pipeline
db.kunden.aggregate([
{
$match: {
"adresse.stadt": "Berlin",
premium: true
}
},
{
$lookup: {
from: "bestellungen",
localField: "_id",
foreignField: "kunden_id",
as: "bestellungen"
}
},
{
$addFields: {
anzahl_bestellungen: { $size: "$bestellungen" },
umsatz: {
$sum: "$bestellungen.gesamtbetrag"
}
}
},
{
$project: {
name: 1,
email: 1,
"adresse.stadt": 1,
interessen: 1,
anzahl_bestellungen: 1,
umsatz: 1,
avg_bestellwert: {
$divide: ["$umsatz", "$anzahl_bestellungen"]
}
}
},
{
$sort: {
umsatz: -1
}
},
{
$limit: 10
}
]);
// Flexible Abfragen mit dynamischen Feldern
db.kunden.find({
$or: [
{ "interessen": "programmieren" },
{ "firma.name": { $exists: true } },
{ alter: { $gte: 30, $lte: 40 } }
],
"adresse.land": "Deutschland"
}).sort({ "name": 1 });
// Text-Suche mit Index
db.kunden.createIndex({ name: "text", "interessen": "text" });
db.kunden.find({
$text: { $search: "programmieren reisen" }
});
3. Use Case Vergleich: E-Commerce Plattform
-- SQL-Ansatz für strukturierte Daten
-- Produkte mit festen Attributen
CREATE TABLE produkte (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
beschreibung TEXT,
preis DECIMAL(10,2) NOT NULL,
kategorie_id INT,
lagerbestand INT DEFAULT 0,
gewicht DECIMAL(5,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (kategorie_id) REFERENCES kategorien(id)
);
-- Kategorien mit Hierarchie
CREATE TABLE kategorien (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
parent_id INT,
ebene INT NOT NULL,
FOREIGN KEY (parent_id) REFERENCES kategorien(id)
);
-- Bestellungen mit ACID-Garantie
CREATE TABLE bestellungen (
id INT PRIMARY KEY AUTO_INCREMENT,
kunden_id INT NOT NULL,
status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending',
gesamtbetrag DECIMAL(10,2) NOT NULL,
bestelldatum TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (kunden_id) REFERENCES kunden(id)
);
BEGIN TRANSACTION;
-- Atomare Bestellabwicklung
INSERT INTO bestellungen (kunden_id, gesamtbetrag, status)
VALUES (123, 299.99, 'paid');
UPDATE produkte
SET lagerbestand = lagerbestand - 1
WHERE id = 456;
INSERT INTO bestellpositionen (bestellung_id, produkt_id, menge, preis)
VALUES (LAST_INSERT_ID(), 456, 1, 299.99);
COMMIT;
// NoSQL-Ansatz für flexible Daten
// Produkte mit variablen Attributen
db.produkte.insertOne({
name: "Smartphone XYZ",
beschreibung: "Modernes Smartphone mit vielen Features",
preis: 599.99,
kategorie: "Elektronik",
lagerbestand: 150,
eigenschaften: {
marke: "TechBrand",
modell: "XYZ Pro",
farbe: ["schwarz", "weiß", "blau"],
speicher: ["64GB", "128GB", "256GB"],
anzeige: {
groesse: "6.1 Zoll",
aufloesung: "1080x2340",
technologie: "OLED"
},
kamera: {
hauptkamera: "48MP",
frontkamera: "12MP",
features: ["Nachtmodus", "Portrait", "4K Video"]
},
konnektivitaet: ["5G", "WiFi 6", "Bluetooth 5.0", "NFC"]
},
bewertungen: [
{
sterne: 5,
kommentar: "Tolles Gerät!",
datum: new Date("2024-01-15")
},
{
sterne: 4,
kommentar: "Gutes Preis-Leistungs-Verhältnis",
datum: new Date("2024-01-20")
}
],
tags: ["smartphone", "5g", "kamera", "premium"]
});
// Flexible Bestellungen mit verschiedenen Zahlungsarten
db.bestellungen.insertOne({
kunden_id: ObjectId("..."),
status: "paid",
gesamtbetrag: 599.99,
positionen: [
{
produkt_id: ObjectId("..."),
produkt_name: "Smartphone XYZ",
variante: {
farbe: "schwarz",
speicher: "128GB"
},
menge: 1,
einzelpreis: 599.99,
gesamtpreis: 599.99
}
],
zahlung: {
methode: "credit_card",
karte: {
typ: "visa",
letzte_zahlen: "1234",
ablaufdatum: "12/25"
},
status: "paid",
transaktions_id: "txn_abc123",
zahlungsdatum: new Date()
},
versand: {
methode: "standard",
adresse: {
name: "Max Mustermann",
strasse: "Hauptstraße 1",
stadt: "Berlin",
plz: "10115",
land: "Deutschland"
},
tracking: {
nummer: "DE123456789",
status: "shipped",
}
},
created: new Date()
});
4. Performance-Vergleich
# Python Performance-Vergleich
import time
import sqlite3
import pymongo
from pymongo import MongoClient
# SQL Performance Test
def sql_performance_test():
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()
# Tabellen erstellen
cursor.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
email TEXT,
age INTEGER
)
''')
# Daten einfügen
start = time.time()
for i in range(10000):
cursor.execute(
'INSERT INTO users (name, email, age) VALUES (?, ?, ?)',
(f'User {i}', f'user{i}@example.com', 20 + i % 50)
)
sql_insert_time = time.time() - start
# Abfragen
start = time.time()
cursor.execute('SELECT * FROM users WHERE age BETWEEN 30 AND 40')
results = cursor.fetchall()
sql_query_time = time.time() - start
conn.close()
return sql_insert_time, sql_query_time, len(results)
# NoSQL Performance Test
def nosql_performance_test():
client = MongoClient('localhost', 27017)
db = client['test_db']
users = db['users']
# Daten einfügen
start = time.time()
documents = []
for i in range(10000):
documents.append({
'name': f'User {i}',
'email': f'user{i}@example.com',
'age': 20 + i % 50
})
users.insert_many(documents)
nosql_insert_time = time.time() - start
# Abfragen
start = time.time()
results = users.find({'age': {'$gte': 30, '$lte': 40}})
count = len(list(results))
nosql_query_time = time.time() - start
client.close()
return nosql_insert_time, nosql_query_time, count
# Performance-Vergleich durchführen
print("Performance-Vergleich:")
sql_insert, sql_query, sql_count = sql_performance_test()
nosql_insert, nosql_query, nosql_count = nosql_performance_test()
print(f"SQL - Insert: {sql_insert:.4f}s, Query: {sql_query:.4f}s, Results: {sql_count}")
print(f"NoSQL - Insert: {nosql_insert:.4f}s, Query: {nosql_query:.4f}s, Results: {nosql_count}")
Entscheidungshilfe: SQL vs NoSQL
Wann SQL verwenden?
Strukturierte Daten:
- Finanzdaten, Buchhaltung
- Kundendaten mit festen Attributen
- Bestellungen mit definierten Feldern
- Inventar mit standardisierten Eigenschaften
ACID-Anforderungen:
- Banktransaktionen
- Buchhaltungssysteme
- E-Commerce Bestellabwicklung
- Reservierungssysteme
Komplexe Abfragen:
- Reporting mit JOINS
- Aggregationen über mehrere Tabellen
- Datenanalyse mit komplexen Filtern
Wann NoSQL verwenden?
Flexible Datenstrukturen:
- Content Management Systeme
- Social Media Posts
- IoT-Sensordaten
- User-Profile mit variablen Attributen
Horizontale Skalierbarkeit:
- Big Data Anwendungen
- Social Networks
- Echtzeit-Analysen
- Microservices
Rapid Prototyping:
- Startups mit schnell ändernden Anforderungen
- MVPs (Minimum Viable Products)
- Agile Entwicklung
Vor- und Nachteile
SQL-Datenbanken
Vorteile:
- ACID-Eigenschaften: Starke Konsistenzgarantien
- Standardisiert: SQL als etablierter Standard
- Werkzeuge: Viele Tools und Frameworks verfügbar
- Datenintegrität: Constraints und Referenzielle Integrität
Nachteile:
- Schema-Rigidität: Änderungen aufwändig
- Skalierbarkeit: Primär vertikale Skalierung
- Performance: Bei sehr großen Datenmengen
- Flexibilität: Begrenzt für unstrukturierte Daten
NoSQL-Datenbanken
Vorteile:
- Flexibles Schema: Leichte Anpassung an neue Anforderungen
- Horizontale Skalierung: Einfache Verteilung auf viele Server
- Performance: Optimiert für große Datenmengen
- Entwicklerfreundlich: JSON-ähnliche Datenstrukturen
Nachteile:
- Konsistenz: Eventual Consistency statt ACID
- Standardisierung: Kein einheitlicher Standard
- Werkzeuge: Weniger ausgereifte Tools
- Komplexität: Transaktionen und JOINS komplexer
Migration Strategien
SQL zu NoSQL Migration
// Schema-Mapping für Migration
const migrationMapping = {
users: {
sql_table: 'users',
nosql_collection: 'users',
fields: {
id: '_id',
name: 'name',
email: 'email',
created_at: 'created'
},
// Transformation rules
transform: (row) => ({
_id: row.id.toString(),
name: row.name,
email: row.email,
created: new Date(row.created_at),
profile: {
age: row.age || null,
preferences: []
}
})
}
};
// Migration Script
async function migrateToNoSQL(sqlConnection, mongoConnection) {
for (const [collectionName, mapping] of Object.entries(migrationMapping)) {
const sqlData = await sqlConnection.query(`SELECT * FROM ${mapping.sql_table}`);
for (const row of sqlData) {
const transformedData = mapping.transform(row);
await mongoConnection.collection(mapping.nosql_collection).insertOne(transformedData);
}
}
}
Häufige Prüfungsfragen
-
Was ist der Hauptunterschied zwischen SQL und NoSQL? SQL: Festes Schema, ACID, relationale Daten. NoSQL: Flexibles Schema, BASE, dokumentenorientierte Daten.
-
Wann würden Sie NoSQL statt SQL wählen? Bei flexiblen Datenstrukturen, horizontaler Skalierbarkeit und Big Data Anforderungen.
-
Erklären Sie ACID vs BASE! ACID: Atomicity, Consistency, Isolation, Durability (starke Konsistenz). BASE: Basically Available, Soft state, Eventually consistent.
-
Was sind die Nachteile von NoSQL-Datenbanken? Weniger etablierte Standards, schwächere Konsistenzgarantien, weniger ausgereifte Werkzeuge.
Wichtigste Quellen
- https://www.mongodb.com/compare/sql-nosql/
- https://www.postgresql.org/about/
- https://dev.mysql.com/doc/refman/8.0/en/