Veröffentlicht am

Was ist SQL? Zweck, Ursprung und Verwendung verstehen

Autoren

    Was ist SQL? Zweck, Ursprung und Verwendung verstehen

    Die meisten modernen Anwendungen, von einfachen Blogs bis zu riesigen sozialen Netzwerken, speichern und lesen Daten mit einer Sprache namens SQL.

    In diesem Artikel erfährst du:

    • Was SQL ist und warum es entwickelt wurde
    • Wie relationale Datenbanken Informationen organisieren
    • Ein konkretes Beispiel mit zwei Tabellen
    • Wie du echte SQL-Abfragen einschließlich JOINs ausführst
    • Warum SQL nach über 50 Jahren noch immer der Standard ist

    Legen wir los.

    Was ist SQL?

    SQL (Structured Query Language) ist eine deklarative Programmiersprache zur Verwaltung relationaler Datenbanken, also Datenbanken, die auf Tabellen, Zeilen und deren Beziehungen aufgebaut sind.

    Mit SQL kannst du:

    • Tabellen erstellen
    • Daten einfügen und ändern
    • Daten mit Filtern abfragen
    • Zusammengehörige Daten aus mehreren Tabellen verknüpfen
    • Bedingungen durchsetzen, etwa eindeutige E-Mail-Adressen oder Fremdschlüssel

    SQL wird in vielen beliebten relationalen Datenbanksystemen verwendet. Hier einige Beispiele:

    • PostgreSQL: Fortschrittliche relationale Open-Source-Datenbank mit umfassender Unterstützung der SQL-Standards.
    • MySQL: In Webanwendungen weit verbreitet, bekannt für Geschwindigkeit und Einfachheit.
    • MariaDB: Von der Community entwickelte Abspaltung von MySQL mit erweiterten Leistungsfunktionen.
    • SQLite: Eine leichtgewichtige, dateibasierte Datenbank, die in mobilen Anwendungen, Browsern und eingebetteten Systemen verwendet wird.
    • Microsoft SQL Server: Kommerzielle Datenbank auf Unternehmensniveau mit leistungsfähigen Werkzeugen für Windows- und Cloud-Umgebungen.
    • Vollständig verwaltete SQL-Dienste in der Cloud, zum Beispiel:
      • Amazon RDS
      • Google Cloud SQL
      • PlanetScale
        Diese Plattformen (Amazon RDS, Google Cloud SQL, PlanetScale) übernehmen Sicherungen, Skalierung, Aktualisierungen und Wartung und bieten dir trotzdem die Leistungsfähigkeit von SQL.

    Warum SQL entwickelt wurde

    In den 1970er-Jahren arbeiteten die Forscher Edgar F. Codd und Donald Chamberlin bei IBM an einer besseren Möglichkeit, Daten zu speichern und abzurufen. Damals waren Datenbanken hierarchisch oder netzwerkbasiert. Der Umgang mit ihnen war:

    • Komplex
    • Fehleranfällig
    • Eng an die physische Speicherung gekoppelt
    • Schwer flexibel abzufragen

    Um das zu lösen, führten sie das relationale Modell ein, das Daten in Tabellen mit klar definierten Beziehungen organisiert.

    SQL wurde entwickelt, weil:

    1. Entwickler eine einfache, lesbare Sprache zum Abfragen von Daten benötigten.
    2. Unternehmen einheitliche Regeln für die Speicherung von Beziehungen benötigten.
    3. Daten ohne Kenntnis interner Speicherdetails zugänglich sein mussten.

    Das Ergebnis?
    SQL wurde 1986 zum Standard (ANSI SQL), und nahezu jede wichtige Datenbank basiert auch heute noch darauf.

    Tabellen, Spalten und Beziehungen verstehen

    In einer relationalen Datenbank:

    • Ist eine Tabelle wie ein Tabellenblatt aufgebaut (Zeilen + Spalten)
    • Stellt eine Zeile einen einzelnen Datensatz dar
    • Stellt eine Spalte eine Eigenschaft dar
    • Identifiziert ein Primärschlüssel jede Zeile eindeutig
    • Verknüpft ein Fremdschlüssel eine Tabelle mit einer anderen

    Sehen wir uns ein einfaches Beispiel mit Benutzern und Bestellungen an.

    Beispielschema: Benutzer und Bestellungen

    Tabelle 1: users

    Tabelle 2: orders

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.99

    Beziehung

    • Jede Bestellung gehört zu einem Benutzer (orders.user_id → users.id).
    • Ein Benutzer kann mehrere Bestellungen haben (1:n-Beziehung).

    Die Tabellen erstellen (SQL-Beispiel)

    CREATE TABLE users (
      id SERIAL PRIMARY KEY,
      name TEXT NOT NULL,
      email TEXT UNIQUE NOT NULL
    );
    
    CREATE TABLE orders (
      id SERIAL PRIMARY KEY,
      user_id INTEGER NOT NULL REFERENCES users(id),
      product TEXT NOT NULL,
      price NUMERIC(10,2) NOT NULL
    );
    

    Damit werden zwei stark typisierte relationale Tabellen mit einer Fremdschlüsselbedingung erstellt. Doch was bedeutet „stark typisiert“? In SQL-Datenbanken wie PostgreSQL und MySQL hat jede Spalte einen strikten Datentyp:

    • Zeichenketten müssen Zeichenketten sein
    • Zahlen müssen Zahlen sein
    • Datumswerte müssen Datumswerte sein
    • Falsche Typen verursachen Fehler

    Das verhindert Fehler wie:

    • „Zwanzig Dollar“ in einer numerischen Spalte speichern
    • 123 in einem E-Mail-Feld speichern
    • Ungültige Datumswerte speichern

    SQLite ist anders: Es ist typflexibel („dynamische Typisierung“), erzwingt Typen also weniger streng. PostgreSQL ist unter den großen Datenbanken am stärksten typisiert.

    Tabellenerstellung Zeile für Zeile erklärt

    Tabelle 1: users

    CREATE TABLE users (
    

    Erklärung: Damit beginnt die Definition einer neuen Tabelle namens users.

    • Funktioniert in: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server
    • „Tabelle“ = strukturierte Menge von Zeilen und Spalten in der Datenbank.
      id SERIAL PRIMARY KEY,
    

    Erklärung:

    • id → Der Spaltenname.

    • SERIAL → Automatisch hochgezählte Ganzzahl. PostgreSQL-spezifisch.

      • Entsprechungen in anderen Engines:

        • MySQL: INT AUTO_INCREMENT
        • SQLite: INTEGER PRIMARY KEY AUTOINCREMENT
    • PRIMARY KEY → Gewährleistet Eindeutigkeit und identifiziert jede Zeile.

      name TEXT NOT NULL,
    

    Erklärung:

    • name → Spalte zum Speichern von Text.
    • TEXT → Text variabler Länge (Zeichenkette).
    • NOT NULL → Darf nicht NULL sein.

    Funktioniert in: PostgreSQL, MySQL, MariaDB, SQLite (SQL Server verwendet jedoch VARCHAR(MAX) statt TEXT).

      email TEXT UNIQUE NOT NULL
    

    Erklärung:

    • email → Speichert die E-Mail-Adresse des Benutzers.
    • TEXT → Texttyp.
    • UNIQUE → Verhindert doppelte E-Mail-Adressen.
    • NOT NULL → Pflichtfeld.

    Funktioniert in: PostgreSQL, MySQL, MariaDB, SQLite (unter Berücksichtigung von Unterschieden bei den Interna des TEXT-Typs).

    );
    

    Erklärung: Schließt die Tabellendefinition ab.

    Tabelle 2: orders

    CREATE TABLE orders (
    

    Beginnt eine neue Tabelle namens orders. Funktioniert in allen wichtigen Engines.

      id SERIAL PRIMARY KEY,
    

    Gleiche Bedeutung wie in der Tabelle users:

    • Automatisch hochgezählter Primärschlüssel
    • PostgreSQL-spezifischer Typ (SERIAL)
    • MySQL verwendet INT AUTO_INCREMENT
    • SQLite verwendet INTEGER PRIMARY KEY AUTOINCREMENT
      user_id INTEGER NOT NULL REFERENCES users(id),
    

    Erklärung:

    • user_id → Eine Zahl, die die Bestellung mit einer Zeile in users verknüpft.
    • INTEGER → Ganzzahl.
    • NOT NULL → Jede Bestellung muss zu einem Benutzer gehören.
    • REFERENCES users(id) → Fremdschlüsselbedingung.

    Bedeutung von Fremdschlüssel:

    • Stellt sicher, dass user_id mit einer tatsächlich vorhandenen id in der Tabelle users übereinstimmen muss.
    • Wird ein Benutzer gelöscht, setzt die Datenbank die Beziehung gemäß den Fremdschlüsselregeln durch.

    Engine-Kompatibilität:

    • PostgreSQL: Vollständig unterstützt.
    • MySQL/MariaDB: Funktioniert, erfordert aber die Speicher-Engine InnoDB.
    • SQLite: Nur bei aktivierter Fremdschlüsselunterstützung unterstützt (PRAGMA foreign_keys = ON;).
      product TEXT NOT NULL,
    

    Speichert den Namen des gekauften Produkts.

    • Typ TEXT → Funktioniert je nach Engine unterschiedlich, speichert aber immer Zeichenketten.
    • Pflichtfeld (NOT NULL).
      price NUMERIC(10,2) NOT NULL
    

    Erklärung:

    • price → Der Preis des Produkts.

    • NUMERIC(10,2) → Zahl mit fester Präzision mit:

      • Bis zu 10 Stellen insgesamt
      • 2 Nachkommastellen

    Das ist ideal für Geldbeträge, weil es Gleitkommafehler vermeidet.

    Engine-Kompatibilität:

    • PostgreSQL: Vollständige Unterstützung.
    • MySQL: DECIMAL(10,2) ist gleichwertig.
    • SQLite: Akzeptiert es, speichert intern jedoch als TEXT, INT oder REAL (dynamische Typisierung).
    );
    

    Beendet die Tabelle.

    Die Beispieldaten einfügen

    Nachdem unsere Tabellen erstellt sind, fügen wir die zuvor gezeigten Beispieldaten ein. Wir geben keine id-Werte manuell an. Die Datenbank generiert sie mit dem Typ SERIAL (automatische Hochzählung) automatisch.

    Die users-Daten einfügen

    INSERT INTO users (name, email)
    VALUES
      ('Alice', 'alice@example.com'),
      ('Bob', 'bob@example.com'),
      ('Caroline', 'caroline@example.com');
    

    Einfügen in users Zeile für Zeile erklärt

    INSERT INTO users (name, email)
    
    • INSERT INTO users Wir fügen der Tabelle users neue Zeilen hinzu.

    • (name, email) Wir geben nur name und email an, nicht id.

      Warum geben wir die ID nicht an? Weil die Spalte id einen Serial-/Auto-Increment-Typ (SERIAL von PostgreSQL) verwendet, erzeugt die Datenbank IDs automatisch:

    • Erster Benutzer → id = 1

    • Zweiter Benutzer → id = 2

    • Dritter Benutzer → id = 3

    Das ist in PostgreSQL, MySQL, MariaDB und SQLite vorhersehbar (mit ihren jeweiligen Mechanismen zur automatischen Hochzählung).

    VALUES
      ('Alice', 'alice@example.com'),
      ('Bob', 'bob@example.com'),
      ('Caroline', 'caroline@example.com');
    
    • VALUES leitet eine Liste einzufügender Zeilen ein.
    • Jede ( ... )-Gruppe entspricht einer neuen Zeile.
    • Die Datenbank weist IDs automatisch in der Reihenfolge zu, in der diese Zeilen erscheinen.

    Die resultierende Tabelle users nach dem Einfügen:

    Obwohl wir keine IDs manuell angegeben haben, gewährleistet die automatische Hochzählung diese Reihenfolge.

    Die orders-Daten einfügen

    Zum Einfügen von Bestellungen in orders müssen wir user_id sehr wohl angeben, weil eine Bestellung zu einem bestimmten Benutzer gehören muss.

    Obwohl wir die Benutzer-IDs nicht manuell festgelegt haben, wissen wir trotzdem, welche sie sind:

    • Alice → ID 1
    • Bob → ID 2
    • Caroline → ID 3

    Sie wurden automatisch in der Einfügereihenfolge erzeugt. Die automatische Hochzählung weist IDs immer fortlaufend zu, solange keine Zeilen gelöscht werden.

    Hier die Einfügeanweisung:

    INSERT INTO orders (user_id, product, price)
    VALUES
      (1, 'Coffee Mug', 12.99),
      (1, 'Hoodie', 39.99),
      (2, 'Stickers', 4.99);
    

    Einfügen in orders Zeile für Zeile erklärt

    INSERT INTO orders (user_id, product, price)
    
    • Wir fügen neue Zeilen in die Tabelle orders ein.

    • Wir geben Folgendes an:

      • user_id → Zu welchem Benutzer die Bestellung gehört
      • product → Name
      • price → Preis

    Beachte erneut: Wir geben id (orders.id) nicht an, weil der Wert automatisch hochgezählt wird.

    VALUES
      (1, 'Coffee Mug', 12.99),
      (1, 'Hoodie', 39.99),
      (2, 'Stickers', 4.99);
    
    • (1, 'Coffee Mug', 12.99)

      • Diese Bestellung gehört zu Benutzer 1 (Alice).
    • (1, 'Hoodie', 39.99)

      • Eine weitere Bestellung für Alice.
    • (2, 'Stickers', 4.99)

      • Bestellung für Benutzer 2 (Bob).

      Woher wissen wir, welcher Benutzer welche ID hat? Das lässt sich so erklären:

    1. Die Benutzer wurden zuerst eingefügt.

    2. Die automatische Hochzählung weist fortlaufende Ganzzahlen zu.

    3. Daher:

      • Erster eingefügter Benutzer → ID 1
      • Zweiter eingefügter Benutzer → ID 2
      • Dritter eingefügter Benutzer → ID 3

    Damit ist die Referenzierung von Benutzern über ihre ID vollständig vorhersehbar.

    Die endgültigen Tabellen

    users

    orders

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.99

    Daten mit SQL abfragen

    Hier sind SQL-Abfragen, die wir auf den beiden zuvor definierten Tabellen ausführen können.

    1. Alle Benutzer auswählen

    SELECT * FROM users;
    

    Ergebnis:

    2. Benutzer mit einem Filter auswählen

    SELECT * FROM users
    WHERE email LIKE '%alice@example.com';
    

    Was das bedeutet:

    • LIKE ist ein SQL-Operator, mit dem Text anhand von Mustern verglichen wird.
    • % ist ein Platzhalter, der beliebig viele Zeichen, einschließlich null Zeichen, abdeckt.

    Also:

    • '%alice@example.com' bedeutet: „Finde jeden Text, der mit alice@example.com endet.“

    Dies liefert die Zeile zurück, in der die E-Mail-Adresse des Benutzers genau Folgendes ist:

    alice@example.com
    

    Es würde aber auch Werte wie diese finden:

    • test-alice@example.com
    • hello.alice@example.com
    • anythingalice@example.com

    Dies gilt, sofern diese Werte vorhanden sind.

    Ergebnis:

    idnameemail
    1Alicealice@example.com

    Diese Abfrage liefert nur den Benutzer zurück, dessen E-Mail-Adresse dem Filter entspricht.

    3. Eine neue Bestellung einfügen

    INSERT INTO orders (user_id, product, price)
    VALUES (2, 'T-Shirt', 29.99);
    

    Die resultierende Tabelle orders nach dem Einfügen:

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.99
    42T-Shirt29.99
    • Die neue Zeile hat die automatisch generierte id = 4.
    • user_id = 2 verknüpft diese Bestellung mit Bob.

    JOINs: Ein leistungsfähiger Teil von SQL

    Mit JOINs kannst du zusammengehörige Daten über mehrere Tabellen hinweg abfragen.

    Sehen wir sie uns anhand unserer Tabellen users und orders an.

    Beispiel 1: INNER JOIN (am häufigsten verwendet)

    Rufe jede Bestellung mit dem Benutzer ab, der sie aufgegeben hat.

    SELECT users.name, orders.product, orders.price
    FROM orders
    INNER JOIN users
      ON orders.user_id = users.id;
    

    Ergebnis:

    nameproductprice
    AliceCoffee Mug12.99
    AliceHoodie39.99
    BobStickers4.99
    BobT-Shirt29.99

    Bob erscheint jetzt zweimal, wegen der zuvor eingefügten neuen Bestellung:

    INSERT INTO orders (user_id, product, price)
    VALUES (2, 'T-Shirt', 29.99);
    

    Beispiel 2: LEFT JOIN

    Rufe alle Benutzer ab, auch diejenigen ohne Bestellungen.

    SELECT users.name, orders.product
    FROM users
    LEFT JOIN orders
      ON users.id = orders.user_id;
    

    Ergebnis:

    nameproduct
    AliceCoffee Mug
    AliceHoodie
    BobStickers
    BobT-Shirt
    Carolinenull

    Caroline erscheint mit null, weil sie keine Bestellungen hat.

    Beispiel 3: Aggregatabfrage

    Der von jedem Benutzer insgesamt ausgegebene Betrag:

    SELECT users.name, SUM(orders.price) AS total_spent
    FROM users
    LEFT JOIN orders ON users.id = orders.user_id
    GROUP BY users.name;
    

    Ergebnis:

    nametotal_spent
    Alice52.98
    Bob34.98
    Carolinenull

    Erklärung

    • Alice: 12.99 + 39.99 = 52.98
    • Bob: 4.99 + 29.99 = 34.98
    • Caroline: Keine Bestellungen → NULL

    Wo du mehr erfahren kannst

    Abschließende Gedanken

    SQL gibt es seit über 50 Jahren, und trotzdem bildet es noch immer die Grundlage der wichtigsten Anwendungen der Welt, weil es:

    • Deklarativ ist
    • Einheitlich ist
    • Vorhersehbar ist
    • Skalierbar ist
    • Auf jahrzehntelanger Forschung basiert

    Sobald du verstehst, wie Tabellen und Joins funktionieren, wird SQL zu einem der leistungsfähigsten Werkzeuge in deinem Programmierwerkzeugkasten.