SemaTrain Logo Ein Fachportal von SemaTrain

Subqueries, Views & Indexe

Jetzt kommt der „Werkzeugkasten“ für echte Praxisabfragen: Subqueries (Unterabfragen), Views (Sichten) und Indexe (Performance-Grundlagen).

Hinweis: Index-Details (Planner/EXPLAIN) sind DB-abhängig – wir bleiben bei robusten Grundlagen.

Lernziel

Nach dieser Lektion können Sie

SQL Grundlagen Schulung – Kursbezug

Dieses Kapitel ist Teil des Lernpfads zur SQL Grundlagen Schulung. Termine & Buchung laufen über SemaTrain.de.

Toolkit Subqueries → Views → Indexe (Performance-Basics).

Worum geht’s?

Schema (Kontext)

Wir bleiben konsistent zum Lernpfad-Schema aus Kapitel 01–05:

Schema-Reminder (SQL)
-- kurse(kurs_id, kurs_name)
-- trainer(trainer_id, name)
-- termine(termin_id, kurs_id, trainer_id, start_datum, ort, format, dauer_tage, preis_eur)
-- buchungen(buchung_id, termin_id, name, email, created_at)

Lehr-/Lernziele

Subqueries

Unterabfragen kommen häufig in „Filter-Fragen“ vor: Zeige X, wenn Bedingung in anderer Tabelle erfüllt ist.

Subquery mit IN (SQL)
-- LZ1: IN-Subquery
-- Alle Kurse, die mindestens einen Termin haben
SELECT k.kurs_id, k.kurs_name
FROM kurse AS k
WHERE k.kurs_id IN (
  SELECT t.kurs_id
  FROM termine AS t
);
Subquery mit EXISTS (SQL)
-- LZ1: EXISTS eignet sich für Existenztests; bei IN/NOT IN müssen NULL-Regeln bewusst berücksichtigt werden.
SELECT k.kurs_id, k.kurs_name
FROM kurse AS k
WHERE EXISTS (
  SELECT 1
  FROM termine AS t
  WHERE t.kurs_id = k.kurs_id
);
Korrelierte Subquery (und warum sie „korreliert“ heißt)

„Korrelierte“ Subqueries referenzieren Spalten der äußeren Query (hier: k.kurs_id). Der Optimizer kann oft trotzdem sehr gut damit umgehen.

Korrelierte Subquery: COUNT je Kurs (SQL)
-- LZ2: Korrelierte Subquery: Termine pro Kurs (Alternative zu GROUP BY)
SELECT
  k.kurs_id,
  k.kurs_name,
  (
    SELECT COUNT(*)
    FROM termine AS t
    WHERE t.kurs_id = k.kurs_id
  ) AS anzahl_termine
FROM kurse AS k
ORDER BY anzahl_termine DESC, k.kurs_name ASC;

Praxis: Häufig ist LEFT JOIN + GROUP BY performanter/üblicher – korrelierte Subqueries sind aber super für „pro Zeile“ Werte.

Views

Views sind gespeicherte Abfragen: gut für Reports, stabile Schnittstellen und weniger Wiederholung.

CREATE VIEW: v_termin_report (SQL)
-- LZ3: View anlegen (Termine + Kurs + optional Trainer)
-- Hinweis: Viele Systeme erlauben kein ORDER BY in der View-Definition.
CREATE VIEW v_termin_report AS
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  t.dauer_tage,
  t.preis_eur,
  k.kurs_id,
  k.kurs_name,
  COALESCE(tr.name, '(noch offen)') AS trainer
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id;
SELECT aus View (SQL)
-- View nutzen wie eine Tabelle
SELECT kurs_name, start_datum, format, ort, trainer
FROM v_termin_report
WHERE format = 'Praesenz'
ORDER BY start_datum ASC, ort ASC;
Views ändern/löschen
DROP VIEW (Basics) (SQL)
-- DB-abhängig:
-- PostgreSQL/MySQL: CREATE OR REPLACE VIEW ...
-- SQLite: ersetzen = DROP + CREATE

DROP VIEW v_termin_report;

Indexe (Performance-Grundlagen)

CREATE INDEX: Join-Spalte (SQL)
-- LZ4: Index auf Foreign-Key/Join-Spalte (typisch sehr sinnvoll)
CREATE INDEX idx_termine_kurs_id ON termine(kurs_id);
CREATE INDEX: WHERE/ORDER BY Spalte (SQL)
-- LZ4: Index auf häufige Filter-/Sortierspalte
CREATE INDEX idx_termine_start_datum ON termine(start_datum);
Optional: Composite Index (Format + Datum) (SQL)
-- Optional: zusammengesetzter Index für typische Query:
-- WHERE format='Online' ORDER BY start_datum
CREATE INDEX idx_termine_format_start_datum ON termine(format, start_datum);
Wann ein Index NICHT hilft
  • Wenn fast alle Zeilen sowieso gelesen werden (sehr unselektive Filter).
  • Bei sehr kleinen Tabellen (Full Scan ist oft günstiger).
  • Wenn die Spalte oft geändert wird (hohe Update-Kosten).
  • Wenn Funktionen auf der Spalte genutzt werden (z. B. LOWER(name)) ohne passenden (funktionalen) Index – DB-abhängig.

Praxisaufgabe

  1. Schreibe eine Subquery mit EXISTS, die alle Kurse liefert, die mindestens einen Präsenz-Termin haben.
  2. Erstelle eine View v_praesenztermine (nur Präsenztermine, inkl. Kursname).
  3. Lege einen Index an, der deine Filter-/Sortier-Query unterstützt (z. B. termine(format, start_datum)).
Lösungsvorschlag anzeigen
Lösung: EXISTS + VIEW + INDEX (SQL)
-- 1) EXISTS: Kurse mit Präsenz-Terminen
SELECT k.kurs_id, k.kurs_name
FROM kurse AS k
WHERE EXISTS (
  SELECT 1
  FROM termine AS t
  WHERE t.kurs_id = k.kurs_id
    AND t.format = 'Praesenz'
);

-- 2) View: nur Präsenz-Termine (Kursname + Termin-Infos)
CREATE VIEW v_praesenztermine AS
SELECT
  k.kurs_name,
  t.termin_id,
  t.start_datum,
  t.ort,
  t.dauer_tage,
  t.preis_eur
FROM kurse AS k
INNER JOIN termine AS t
  ON t.kurs_id = k.kurs_id
WHERE t.format = 'Praesenz';

-- 3) Index: typische Filter+Sortierung
CREATE INDEX idx_termine_format_start_datum ON termine(format, start_datum);

Kurz-Takeaways

Primärreferenzen

Geprüfte SQL-Referenzen

Die fachlichen Aussagen dieser Lektion werden gegen Normkontext und zuständige Primärdokumentationen der jeweiligen Datenbanksysteme geprüft. Erklärungen, Beispiele, Übungen, Quizfragen und Lösungen sind eigenständige SemaTrain-Inhalte.

Lektionsprofil: Subqueries, Views und Indexe

Kursbasis: Gemeinsamer SQL-Kern mit explizit gekennzeichneten Varianten für SQLite 3.53.4, PostgreSQL 18 und MySQL 8.4 LTS.

Quellenprüfung: 2026-09-18

Einordnung: Subqueries, Views und Indexe besitzen einen gemeinsamen SQL-Kern, während View-Ersetzung, Planner und Indexdetails produktabhängig sind.

ISO/IEC 9075-1:2023 Information technology — Database languages SQL — Part 1: Framework

Quellentyp: Norm-/Standardreferenz

Projekt / Referenzstelle: ISO/IEC 9075

Ersteller / Maintainer: ISO/IEC JTC 1/SC 32

Versionsbezug: ISO/IEC 9075-1:2023

Aussagebereich: SQL-Framework, Begriffe und Standardkontext.

Offizielle Primärreferenz

SQL As Understood By SQLite

Quellentyp: Projekt-Primärdokumentation

Projekt / Referenzstelle: SQLite

Ersteller / Maintainer: SQLite Developers

Versionsbezug: SQLite 3.53.4

Aussagebereich: SQLite-Sprachübersicht zu SELECT/Subqueries, CREATE VIEW und CREATE INDEX.

Offizielle Primärreferenz

PostgreSQL 18 Subquery Expressions

Quellentyp: Projekt-Primärdokumentation

Projekt / Referenzstelle: PostgreSQL

Ersteller / Maintainer: PostgreSQL Global Development Group

Versionsbezug: PostgreSQL 18

Aussagebereich: EXISTS, IN und weitere Subquery-Ausdrücke.

Offizielle Primärreferenz

PostgreSQL 18 CREATE VIEW

Quellentyp: Projekt-Primärdokumentation

Projekt / Referenzstelle: PostgreSQL

Ersteller / Maintainer: PostgreSQL Global Development Group

Versionsbezug: PostgreSQL 18

Aussagebereich: CREATE VIEW und CREATE OR REPLACE VIEW.

Offizielle Primärreferenz

PostgreSQL 18 Introduction to Indexes

Quellentyp: Projekt-Primärdokumentation

Projekt / Referenzstelle: PostgreSQL

Ersteller / Maintainer: PostgreSQL Global Development Group

Versionsbezug: PostgreSQL 18

Aussagebereich: Index-Grundidee, Nutzen und Kosten.

Offizielle Primärreferenz

MySQL 8.4 Subqueries

Quellentyp: Hersteller-Primärdokumentation

Projekt / Referenzstelle: MySQL

Ersteller / Maintainer: Oracle Corporation

Versionsbezug: MySQL 8.4 LTS

Aussagebereich: MySQL-Subquery-Syntax und -Semantik.

Offizielle Primärreferenz

MySQL 8.4 CREATE VIEW Statement

Quellentyp: Hersteller-Primärdokumentation

Projekt / Referenzstelle: MySQL

Ersteller / Maintainer: Oracle Corporation

Versionsbezug: MySQL 8.4 LTS

Aussagebereich: CREATE VIEW und CREATE OR REPLACE VIEW.

Offizielle Primärreferenz

MySQL 8.4 CREATE INDEX Statement

Quellentyp: Hersteller-Primärdokumentation

Projekt / Referenzstelle: MySQL

Ersteller / Maintainer: Oracle Corporation

Versionsbezug: MySQL 8.4 LTS

Aussagebereich: CREATE INDEX und MySQL-Indexoptionen.

Offizielle Primärreferenz

SQL Quellen und Methodik

Quiz: Subqueries, Views & Indexe

1. (LZ1) Wofür eignet sich EXISTS besonders?

Einfachauswahl – genau eine Antwort ist richtig.

2. (LZ2) Was ist eine korrelierte Subquery?

Einfachauswahl – genau eine Antwort ist richtig.

3. (LZ3) Was ist eine View am ehesten?

Einfachauswahl – genau eine Antwort ist richtig.

4. (LZ4) Welche Aussage zu Indexen stimmt?

Einfachauswahl – genau eine Antwort ist richtig.

Praxisaufgabe

Mini-Projekt: Reporting-View „Termin-Report“ + Index-Setup (Toolkit)

Sie bauen eine reporttaugliche Sicht (View) auf Basis der bisherigen Kapitel: Termine + Kursname + optional Trainer. Dazu kommen 2 typische Subquery-Fälle (EXISTS) und ein sinnvolles Minimal-Index-Set für Filter/Sortierung.

Beitrag zu den Lehr-/Lernzielen: LZ1 (IN/EXISTS), LZ2 (korreliert), LZ3 (VIEW), LZ4 (INDEX).
Praxis-Stack Subquery → View → Index

Stufe A – Subqueries (Filter-Fragen)

Stufe B – View (reporttaugliche „virtuelle Tabelle“)

Stufe C – Indexe (sinnvoll & minimal)

Musterlösung (Subqueries + View + Indexe)
Projektlösung: v_termin_report + EXISTS + Indexe (SQL)
-- ==========================================================
-- Stufe A – Subqueries (EXISTS)
-- ==========================================================

-- (A1) Alle Kurse, die mindestens einen Termin haben
SELECT k.kurs_id, k.kurs_name
FROM kurse AS k
WHERE EXISTS (
  SELECT 1
  FROM termine AS t
  WHERE t.kurs_id = k.kurs_id
)
ORDER BY k.kurs_name ASC;

-- (A2) Alle Kurse, die mindestens einen ONLINE-Termin haben
SELECT k.kurs_id, k.kurs_name
FROM kurse AS k
WHERE EXISTS (
  SELECT 1
  FROM termine AS t
  WHERE t.kurs_id = k.kurs_id
    AND t.format = 'Online'
)
ORDER BY k.kurs_name ASC;

-- ==========================================================
-- Stufe B – View: Report „Termine + Kurs + Trainer (optional)“
-- SQLite: ersetzen = DROP VIEW + CREATE VIEW
-- ==========================================================

-- DROP VIEW v_termin_report;

CREATE VIEW v_termin_report AS
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  t.dauer_tage,
  t.preis_eur,
  k.kurs_id,
  k.kurs_name,
  COALESCE(tr.name, '(noch offen)') AS trainer
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id;

-- View nutzen: Online-Terminsuche
SELECT termin_id, start_datum, ort, kurs_name, trainer, preis_eur
FROM v_termin_report
WHERE format = 'Online'
ORDER BY start_datum ASC, ort ASC;

-- ==========================================================
-- Stufe C – Indexe (Performance-Basics)
-- ==========================================================

CREATE INDEX idx_termine_kurs_id
ON termine(kurs_id);

CREATE INDEX idx_termine_start_datum
ON termine(start_datum);

-- Optional: häufiges Muster (Format + Datum)
CREATE INDEX idx_termine_format_start_datum
ON termine(format, start_datum);

-- Hinweis: zu viele Indexe verlangsamen INSERT/UPDATE/DELETE.

Ausblick: Im nächsten Kapitel (Transaktionen) kombinieren wir „Safe-Change“ (Kap. 05) mit echten Mehrschritt-Änderungen – und sehen, warum DML ohne Transaktion riskant ist.