Skip to content
IRC-Coding IRC-Coding
SQL NoSQL Vergleich Relationale Datenbanken Dokumentenorientierte DB MongoDB MySQL PostgreSQL

SQL vs NoSQL: Relationale vs Dokumentenorientierte Datenbanken & Use Cases

Vergleich von SQL und NoSQL Datenbanken. Relationale (MySQL, PostgreSQL) vs Dokumentenorientierte (MongoDB) mit Use Cases, Vor- und Nachteilen.

S

schutzgeist

2 min read

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

  1. SQL-Datenbanken: Tabellen, Schema, JOINS, ACID
  2. NoSQL-Datenbanken: Dokumente, Collections, flexible Schema
  3. Datenmodell: Relational vs Dokumentenorientiert
  4. Abfragesprachen: SQL vs NoSQL Query Language
  5. Skalierbarkeit: Vertikal vs Horizontal
  6. Konsistenzmodelle: ACID vs BASE
  7. Use Cases: Strukturierte vs Flexible Daten
  8. 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

  1. Was ist der Hauptunterschied zwischen SQL und NoSQL? SQL: Festes Schema, ACID, relationale Daten. NoSQL: Flexibles Schema, BASE, dokumentenorientierte Daten.

  2. Wann würden Sie NoSQL statt SQL wählen? Bei flexiblen Datenstrukturen, horizontaler Skalierbarkeit und Big Data Anforderungen.

  3. Erklären Sie ACID vs BASE! ACID: Atomicity, Consistency, Isolation, Durability (starke Konsistenz). BASE: Basically Available, Soft state, Eventually consistent.

  4. Was sind die Nachteile von NoSQL-Datenbanken? Weniger etablierte Standards, schwächere Konsistenzgarantien, weniger ausgereifte Werkzeuge.

Wichtigste Quellen

  1. https://www.mongodb.com/compare/sql-nosql/
  2. https://www.postgresql.org/about/
  3. https://dev.mysql.com/doc/refman/8.0/en/
Zurück zum Blog
Share: