{
  "assessmentTests": {
    "sql_test": {
      "name": "SQL-Test",
      "desc": "30 Szenariofragen zu Filterung, Joins, Aggregation, Subqueries und Window Functions — erfahre, ob dein SQL das bedeutet, was eine Stellenanzeige unter SQL-Kenntnissen versteht.",
      "recommendation": "Dein SQL-Kenntnisprofil",
      "results": {
        "beginner": {
          "name": "Anfänger",
          "desc": "Du kannst eine funktionsfähige SELECT-Anweisung schreiben und nach einigen Bedingungen filtern, aber die Fragen, die du verpasst hast, konzentrieren sich auf NULL-Behandlung und Join-Mechanik statt auf Syntax — eine Spalte mit = gegen NULL vergleichen, was LEFT JOIN tatsächlich erhält, wie die Grenzen von BETWEEN funktionieren. Das hat nichts mit schlechten Datenbankfähigkeiten zu tun; das sind spezifische Regeln, die Menschen, die SQL durch Ausprobieren gelernt haben, genauso oft verwirren wie Anfänger. Sie sind wichtig bei der Arbeit, weil jede davon ein Ort ist, wo eine Query eine plausibel aussehende, aber falsche Zeilenanzahl zurückgibt.",
          "recommendation": "Beginne mit drei Dingen in dieser Reihenfolge: warum WHERE col = NULL nie etwas zutreffendes ergibt (und IS NULL erforderlich ist), wie INNER JOIN nicht zutreffende Zeilen löscht während LEFT JOIN sie behält, und die genauen Grenzen, die BETWEEN einschließt. Die SQL-Anleitung von Mode Analytics und die PostgreSQL-Dokumentation behandeln alle drei mit ausführbaren Beispielen."
        },
        "intermediate": {
          "name": "Mittelstufe",
          "desc": "Du bewältigst alltägliche Reporting-Queries mühelos — Joins, GROUP BY, einfaches Filtern — und würdest durch Routine-Dashboard- oder Ad-hoc-Analysearbeit nicht verlangsamt. Der Unterschied zwischen hier und Advanced liegt hauptsächlich darin, was passiert, wenn Klauseln interagieren: eine WHERE-Klausel auf der Tabelle des Joins, die einen LEFT JOIN stillschweigend in einen INNER JOIN zurückverwandelt, ein Join, der die Zeilenanzahl auffächert, bevor ein Aggregat auf sie angewendet wird, ein NOT IN, das stillschweigend nichts zurückgibt, weil ein NULL in die Subquery gerutscht ist. Das sind die Art von Bugs, die eine schnelle Sichtprüfung bestehen und nur sichtbar werden, wenn jemand den Gesamtwert mit einem anderen Bericht vergleicht.",
          "recommendation": "Konzentriere dich darauf, wie Klauseln interagieren statt nur darauf, was jede einzelne tut: WHERE-Filterung auf den rechten Spalten eines LEFT JOIN, Zeilenmultiplikation aus One-to-Many-Joins bevor du aggregierst, und warum NOT IN bei Vorhandensein von NULLs nicht funktioniert (EXISTS tut es normalerweise). Dann HAVING gegen WHERE, da diese Unterscheidung Menschen verwirrt, die beide Klauseln einzeln bereits kennen."
        },
        "advanced": {
          "name": "Fortgeschritten",
          "desc": "Dies ist das Niveau, das die meisten Stellenanzeigen unter \"starkem SQL\" verstehen. Du liest eine Multi-Join-Query und kannst ihre Zeilenanzahl vor dem Ausführen vorhersagen, du weißt, warum sich ein Resultset geändert hat statt nur, dass es das tat, und du greifen zu einer CTE oder einer Window Function statt zu einer verschachtelten Subquery, wenn das das klarere Werkzeug ist. Was diese Gruppe von der Spitze trennt, ist die defensive Seite der Arbeit: zu wissen, welche Ranking-Funktion zu verwenden ist, wenn Gleichstände wichtig sind, was eine Transaktion von einer gleichzeitigen Sitzung isoliert, und was eine fehlende ON DELETE-Regel auf Datenbankebene tatsächlich tut.",
          "recommendation": "Drücke in die Teile, die Daten schützen, die auch andere Menschen anfassen: der Unterschied zwischen RANK, DENSE_RANK und ROW_NUMBER bei Gleichständen, was COMMIT tatsächlich sichtbar macht und für wen, und Fremdschlüsselverhalten bei DELETE. Nutze The-Index-Newsletter oder SQL-for-Devs Deep Dives auf Window Functions und Transaction Isolation als nächste Station für beides."
        },
        "expert": {
          "name": "Experte",
          "desc": "Du hast in jedem Abschnitt die höchste Punktzahl erreicht — Filterung und NULL-Semantik, Joins und Set-Operationen, Aggregation und Subqueries, und Window Functions, CTEs und Constraints. Praktisch bedeutet das, dass du eine unbekannte Multi-Join-Reporting-Query bekommst und erklären kannst, warum sie die Zeilenanzahl zurückgibt, die sie tut, nicht nur was die Syntax sagen sollte, was die schwierigere und wertvollere Fähigkeit ist. Auf dieser Ebene ist die Query-Sprache selten der einschränkende Faktor; die Grenze ist normalerweise das Schemadesign oder die Datengröße darunter.",
          "recommendation": "Die Erträge sind jetzt in Ausführungsplänen und Design: EXPLAIN-Ausgabe lesen, bevor du annimmst, eine Query ist langsam, Indizierungsstrategie als Kompromiss gegen Schreibkosten statt als kostenlos gewonnen, und Normalisierungsentscheidungen, die halten, während ein Schema wächst. Wenn du für eine Rolle überprüft wirst, beschreibe einen Query-Bug wie den Join-Fan-Out oder die NOT IN/NULL-Falle, die du in der Produktion gefunden hast, statt SQL-Features zu nennen — das demonstriert das Denken, nicht nur das Vokabular."
        }
      },
      "questions": [
        {
          "question": "Eine Tabelle hat eine nullable Spalte age. Du führst SELECT * FROM users WHERE age = NULL aus. Wie viele Zeilen gibt das zurück, auch wenn mehrere Zeilen age auf NULL gesetzt haben?",
          "options": [
            {
              "icon": "",
              "label": "Jede Zeile, wo age NULL ist, weil = NULL wie jeden anderen Wert zutreffend macht"
            },
            {
              "icon": "",
              "label": "Null — der Vergleich von irgendetwas zu NULL mit = erzeugt UNKNOWN, nie TRUE, also treffen keine Zeilen zu; IS NULL ist erforderlich"
            },
            {
              "icon": "",
              "label": "Ein Syntax-Fehler — NULL kann nicht auf der rechten Seite von = stehen"
            },
            {
              "icon": "",
              "label": "Jede Zeile in der Tabelle, weil NULL-Vergleiche standardmäßig TRUE ergeben"
            }
          ]
        },
        {
          "question": "Du führst SELECT DISTINCT department, role FROM employees aus. Wovon entfernt DISTINCT Duplikate?",
          "options": [
            {
              "icon": "",
              "label": "Der Kombination von department und role zusammen — eine Zeile wird nur entfernt, wenn beide Spalten exakt eine andere Zeile abgleichen"
            },
            {
              "icon": "",
              "label": "Nur von Duplikat-Werten in department, behält jede role"
            },
            {
              "icon": "",
              "label": "Nur von Duplikat-Werten in role, behält jeden department"
            },
            {
              "icon": "",
              "label": "Gar nichts — DISTINCT funktioniert nur mit einer einzelnen Spalte"
            }
          ]
        },
        {
          "question": "Eine Spalte name wird gefiltert mit WHERE name LIKE 'A_'. Welche dieser Werte treffen zu: 'A', 'Al', 'Ana', 'Ally'?",
          "options": [
            {
              "icon": "",
              "label": "'A' — der Unterstrich ist optional und trifft null oder mehr Zeichen"
            },
            {
              "icon": "",
              "label": "'Al' — der Unterstrich trifft genau ein Zeichen, also trifft LIKE 'A_' jeden zweistelligen String, der mit A anfängt"
            },
            {
              "icon": "",
              "label": "'Ana' und 'Ally' — der Unterstrich trifft beliebig viele nachfolgende Zeichen"
            },
            {
              "icon": "",
              "label": "Alle vier Werte treffen zu, weil LIKE die Länge ignoriert"
            }
          ]
        },
        {
          "question": "Eine price-Spalte wird gefiltert mit WHERE price BETWEEN 10 AND 20. Werden Zeilen mit price genau 10 oder genau 20 einschließlich?",
          "options": [
            {
              "icon": "",
              "label": "Nein — BETWEEN schließt beide Endpunkte aus"
            },
            {
              "icon": "",
              "label": "Ja — BETWEEN ist auf beiden Enden einschließlich, äquivalent zu price >= 10 AND price <= 20"
            },
            {
              "icon": "",
              "label": "Nur price = 10 ist einschließlich; die obere Grenze ist ausschließlich"
            },
            {
              "icon": "",
              "label": "Nur price = 20 ist einschließlich; die untere Grenze ist ausschließlich"
            }
          ]
        },
        {
          "question": "Eine Tabelle orders hat 10 Zeilen, und 3 davon haben ein NULL shipped_date. Was gibt SELECT COUNT(*) FROM orders zurück, und was gibt SELECT COUNT(shipped_date) FROM orders zurück?",
          "options": [
            {
              "icon": "",
              "label": "10, dann 10 — COUNT zählt immer Zeilen unabhängig von NULLs"
            },
            {
              "icon": "",
              "label": "7, dann 7 — beide Formen überspringen NULL-Zeilen"
            },
            {
              "icon": "",
              "label": "10, dann 7 — COUNT(*) zählt jede Zeile, COUNT(column) zählt nur Zeilen, wo diese Spalte nicht NULL ist"
            },
            {
              "icon": "",
              "label": "10, dann 3 — COUNT(column) zählt nur die NULL-Werte"
            }
          ]
        },
        {
          "question": "Du führst SELECT name, salary FROM employees ORDER BY 2 DESC aus. Worauf bezieht sich die 2?",
          "options": [
            {
              "icon": "",
              "label": "Ein Syntax-Fehler — ORDER BY akzeptiert nur Spaltennamen, keine Nummern"
            },
            {
              "icon": "",
              "label": "Die zweite Zeile des Resultats"
            },
            {
              "icon": "",
              "label": "Ein Literalwert von 2, als Tiebreaker verwendet"
            },
            {
              "icon": "",
              "label": "Die zweite Spalte in der SELECT-Liste, salary — ORDER BY akzeptiert die Positionsnummer einer Spalte als Kurzform"
            }
          ]
        },
        {
          "question": "In Standard-SQL, worauf wird 'Ana' || ' ' || 'Lee' bewertet?",
          "options": [
            {
              "icon": "",
              "label": "Ein Syntax-Fehler — SQL hat keinen Verkettungsoperator"
            },
            {
              "icon": "",
              "label": "3 — || wird als boolesches OR behandelt und gibt eine Anzahl zurück"
            },
            {
              "icon": "",
              "label": "'AnaLee' — || verkettet, ohne Leerzeichen zu bewahren"
            },
            {
              "icon": "",
              "label": "'Ana Lee' — || ist der Standard-SQL-String-Verkettungsoperator"
            }
          ]
        },
        {
          "question": "Welche dieser ist genau äquivalent zu WHERE status IN ('open', 'pending', 'review')?",
          "options": [
            {
              "icon": "",
              "label": "WHERE status = 'open' AND status = 'pending' AND status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status != 'open' OR status != 'pending' OR status != 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status = 'open' OR status = 'pending' OR status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status LIKE 'open,pending,review'"
            }
          ]
        },
        {
          "question": "customers hat 100 Zeilen; 20 davon haben nie eine Bestellung platziert. Du führst SELECT c.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id aus. Erscheinen die 20 Kunden ohne Bestellungen im Resultat?",
          "options": [
            {
              "icon": "",
              "label": "Ja, je einmal, mit o.id als NULL gezeigt"
            },
            {
              "icon": "",
              "label": "Ja, aber nur wenn sie auch in einer WHERE-Klausel erscheinen"
            },
            {
              "icon": "",
              "label": "Nein — INNER JOIN gibt nur Zeilen zurück, die in beiden Tabellen eine Übereinstimmung haben, also werden Kunden ohne Bestellungen vollständig gelöscht"
            },
            {
              "icon": "",
              "label": "Ja, dupliziert je einmal pro Spalte in orders"
            }
          ]
        },
        {
          "question": "Du willst jeden Kunden, unabhängig davon, ob sie Bestellungen haben, also schreibst du SELECT c.id, o.total FROM customers c LEFT JOIN orders o ON c.id = o.customer_id WHERE o.total > 100. Gibt das immer noch Kunden ohne Bestellungen zurück?",
          "options": [
            {
              "icon": "",
              "label": "Nein — das Filtern auf o.total in der WHERE-Klausel verwirft die NULL-Zeilen, die LEFT JOIN für nicht übereinstimmende Kunden produzierte, also verhält sich das wie ein INNER JOIN"
            },
            {
              "icon": "",
              "label": "Ja — LEFT JOIN bewahrt immer jede Zeile aus customers, egal was folgt"
            },
            {
              "icon": "",
              "label": "Ja, mit o.total als 0 für Kunden ohne Bestellungen gezeigt"
            },
            {
              "icon": "",
              "label": "Nein — LEFT JOIN konvertiert sich stillschweigend selbst in einen RIGHT JOIN, wenn eine WHERE-Klausel hinzugefügt wird"
            }
          ]
        },
        {
          "question": "Eine employees-Tabelle hat eine id-Spalte und eine manager_id-Spalte, die auf eine andere Zeile's id zeigt. Um jeden Mitarbeiter neben seinem Manager's Namen aufzulisten, bindest du die Tabelle an sich selbst: SELECT e.name, m.name FROM employees e JOIN employees m ON e.manager_id = m.id. Wie heißt dieses Muster?",
          "options": [
            {
              "icon": "",
              "label": "Ein Cross Join — jeder Mitarbeiter wird mit jedem Manager abgeglichen"
            },
            {
              "icon": "",
              "label": "Ein Recursive Join — er ruft die gesamte Managementkette ab"
            },
            {
              "icon": "",
              "label": "Ein Self-Join — dieselbe Tabelle wird an sich selbst mit zwei verschiedenen Aliasen angebunden"
            },
            {
              "icon": "",
              "label": "Das ist ungültiges SQL — eine Tabelle kann nicht an sich selbst angebunden werden"
            }
          ]
        },
        {
          "question": "Zwei SELECT-Queries mit denselben Spalten werden mit UNION kombiniert. Wenn beide Queries eine identische Zeile zurückgeben, wie viele Kopien dieser Zeile erscheinen im Endergebnis?",
          "options": [
            {
              "icon": "",
              "label": "Eine — UNION entfernt doppelte Zeilen über das kombinierte Resultat; UNION ALL würde beide Kopien behalten"
            },
            {
              "icon": "",
              "label": "Zwei — UNION behält jede Zeile aus beiden Queries"
            },
            {
              "icon": "",
              "label": "Null — UNION entfernt jede Zeile, die in beiden Queries erscheint"
            },
            {
              "icon": "",
              "label": "Das hängt davon ab, welche Query die Zeile zuerst aufgelistet hat"
            }
          ]
        },
        {
          "question": "Tabelle sizes hat 3 Zeilen und colors hat 4 Zeilen. Wie viele Zeilen gibt SELECT * FROM sizes CROSS JOIN colors zurück?",
          "options": [
            {
              "icon": "",
              "label": "7 — CROSS JOIN addiert die Zeilenzahlen zusammen"
            },
            {
              "icon": "",
              "label": "12 — ein CROSS JOIN gibt jede mögliche Kombination von Zeilen aus beiden Tabellen zurück (3 x 4)"
            },
            {
              "icon": "",
              "label": "3 — CROSS JOIN gibt eine Zeile pro Zeile in der ersten Tabelle zurück"
            },
            {
              "icon": "",
              "label": "0 — CROSS JOIN erfordert eine ON-Bedingung oder gibt nichts zurück"
            }
          ]
        },
        {
          "question": "orders hat 1 Zeile für Bestellung #100, und order_items hat 3 Zeilen für Bestellung #100 (je eine pro Position). Du führst SELECT o.id, o.total FROM orders o JOIN order_items i ON o.id = i.order_id WHERE o.id = 100 aus. Wie viele Zeilen kommen für Bestellung #100 zurück?",
          "options": [
            {
              "icon": "",
              "label": "3 — der Join produziert eine Ausgabezeile pro übereinstimmender order_items-Zeile, also wird die einzelne Zeile der Bestellung #100 je pro Position wiederholt"
            },
            {
              "icon": "",
              "label": "1 — orders hat nur eine Zeile für Bestellung #100, also kann der Join nicht mehr produzieren"
            },
            {
              "icon": "",
              "label": "4 — eine Zeile für die Bestellung plus eine pro Position"
            },
            {
              "icon": "",
              "label": "0 — das Binden einer einzeiligen Tabelle an eine dreizellige Tabelle auf einem nicht eindeutigen Schlüssel schlägt fehl"
            }
          ]
        },
        {
          "question": "SELECT c.name, o.id FROM customers c RIGHT JOIN orders o ON c.id = o.customer_id gibt dasselbe zurück wie welche dieser?",
          "options": [
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o LEFT JOIN customers c ON c.id = o.customer_id — die Tabellenreihenfolge zu tauschen und statt RIGHT JOIN LEFT JOIN zu verwenden ist äquivalent"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c LEFT JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o CROSS JOIN customers c"
            }
          ]
        },
        {
          "question": "In Standard-SQL führst du SELECT department, name, AVG(salary) FROM employees GROUP BY department aus. Ist das eine gültige Query?",
          "options": [
            {
              "icon": "",
              "label": "Ja — GROUP BY muss nur department einschließen, weil es zuerst aufgelistet wird"
            },
            {
              "icon": "",
              "label": "Nein — name ist gewählt aber weder aggregiert noch aufgelistet in GROUP BY, und Standard-SQL erfordert, dass jede nicht-aggregierte gewählte Spalte in der GROUP BY-Klausel erscheint"
            },
            {
              "icon": "",
              "label": "Ja — SQL wählt automatisch einen willkürlichen Namen pro Abteilung"
            },
            {
              "icon": "",
              "label": "Nein — AVG() kann nicht mit GROUP BY in derselben Query kombiniert werden"
            }
          ]
        },
        {
          "question": "Du willst Abteilungen, deren Durchschnittssalär 80000 übersteigt. Welche Klausel filtert auf einen aggregierten Wert wie AVG(salary) nach der Grouping — WHERE oder HAVING?",
          "options": [
            {
              "icon": "",
              "label": "WHERE — HAVING wird nur mit UNION-Queries verwendet"
            },
            {
              "icon": "",
              "label": "Beide funktionieren identisch mit Aggregatfunktionen"
            },
            {
              "icon": "",
              "label": "Keines — Aggregatfilterung erfordert eine Subquery"
            },
            {
              "icon": "",
              "label": "HAVING — WHERE filtert einzelne Zeilen vor der Grouping passiert, HAVING filtert Gruppen nach der Aggregation"
            }
          ]
        },
        {
          "question": "Du schreibst SELECT salary * 1.1 AS new_salary FROM employees WHERE new_salary > 50000. Läuft das?",
          "options": [
            {
              "icon": "",
              "label": "Ja — Aliase, die in SELECT definiert wurden, sind immer für WHERE in derselben Query verfügbar"
            },
            {
              "icon": "",
              "label": "Ja, aber nur für numerische Aliase"
            },
            {
              "icon": "",
              "label": "Nein — AS ist nicht erlaubt innerhalb einer WHERE-gefilterten Query"
            },
            {
              "icon": "",
              "label": "Nein — WHERE wird bewertet, bevor SELECT den Alias new_salary zuweist, also existiert der Alias zu diesem Punkt in der Ausführung noch nicht"
            }
          ]
        },
        {
          "question": "SELECT name FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE department = e.department). Warum heißt das eine Correlated Subquery?",
          "options": [
            {
              "icon": "",
              "label": "Weil sie einen JOIN statt einer WHERE-Klausel verwendet"
            },
            {
              "icon": "",
              "label": "Die innere Subquery referenziert e.department aus der äußeren Query, also muss sie für jede Zeile, die die äußere Query in Betracht zieht, neu bewertet werden"
            },
            {
              "icon": "",
              "label": "Weil sie mehr als eine Spalte zurückgibt"
            },
            {
              "icon": "",
              "label": "Weil sie genau einmal läuft, bevor die äußere Query anfängt"
            }
          ]
        },
        {
          "question": "Eine Subquery SELECT manager_id FROM employees gibt einige NULL-Werte zusammen mit echten IDs zurück. Du führst SELECT name FROM employees WHERE id NOT IN (SELECT manager_id FROM employees) aus. Was passiert?",
          "options": [
            {
              "icon": "",
              "label": "Sie gibt jeden Mitarbeiter zurück, der kein Manager ist, ignoriert die NULLs"
            },
            {
              "icon": "",
              "label": "Sie gibt null Zeilen zurück — ein einzelnes NULL in der NOT IN Liste macht jeden Vergleich UNKNOWN, also keine Zeile kann die Bedingung erfüllen"
            },
            {
              "icon": "",
              "label": "Sie wirft einen Fehler aus, weil NOT IN nicht mit Subqueries verwendet werden kann"
            },
            {
              "icon": "",
              "label": "Sie gibt jeden Mitarbeiter zurück, da NULL als Wildcard behandelt wird"
            }
          ]
        },
        {
          "question": "SELECT name FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id). Was gibt das zurück?",
          "options": [
            {
              "icon": "",
              "label": "Jeden Kunden, der mindestens eine Zeile in orders hat — EXISTS prüft nur, ob die Subquery irgendwelche Zeilen zurückgibt, nicht welche Werte sie enthalten"
            },
            {
              "icon": "",
              "label": "Jeden Kunden, weil SELECT 1 immer wahr zurückgibt"
            },
            {
              "icon": "",
              "label": "Ein Fehler, weil die Subquery eine Nummer statt eines Spaltennamens auswählt"
            },
            {
              "icon": "",
              "label": "Nur Kunden mit genau einer Bestellung"
            }
          ]
        },
        {
          "question": "Du schreibst SELECT name, (SELECT order_id FROM orders WHERE customer_id = c.id) AS last_order FROM customers c, und ein gegebener Kunde hat 3 Zeilen in orders. Was passiert, wenn diese Query läuft?",
          "options": [
            {
              "icon": "",
              "label": "Sie gibt die erste zutreffende order_id zurück und ignoriert stillschweigend die anderen zwei"
            },
            {
              "icon": "",
              "label": "Sie gibt eine komma-getrennte Liste aller drei order_ids zurück"
            },
            {
              "icon": "",
              "label": "Sie gibt 3 Zeilen für diesen Kunden zurück, eine pro Bestellung"
            },
            {
              "icon": "",
              "label": "Sie wirft zur Laufzeit einen Fehler aus — eine Skalar-Subquery in der SELECT-Liste muss höchstens eine Zeile zurückgeben, und diese gibt drei zurück"
            }
          ]
        },
        {
          "question": "Vier Zeilen binden für den höchsten Score. Mit RANK() ORDER BY score DESC bekommen alle vier Rang 1. Welchen Rang bekommt die nächste Zeile?",
          "options": [
            {
              "icon": "",
              "label": "5 — RANK() hinterlässt eine Lücke gleich der Anzahl der gebundenen Zeilen bevor es weitermacht"
            },
            {
              "icon": "",
              "label": "2 — RANK() erhöht sich immer genau um eins nach irgendeiner Bindung"
            },
            {
              "icon": "",
              "label": "1 — jede nachfolgende Zeile bekommt auch Rang 1"
            },
            {
              "icon": "",
              "label": "4 — RANK() startet das Zählen von der Anzahl der Bindungen neu"
            }
          ]
        },
        {
          "question": "Vier Zeilen binden für den höchsten Score. Mit DENSE_RANK() ORDER BY score DESC bekommen alle vier Rang 1. Welchen Rang bekommt die nächste Zeile?",
          "options": [
            {
              "icon": "",
              "label": "5 — DENSE_RANK() verhält sich genau wie RANK()"
            },
            {
              "icon": "",
              "label": "1 — DENSE_RANK() weist jeder verbleibenden Zeile denselben Rang zu"
            },
            {
              "icon": "",
              "label": "2 — DENSE_RANK() hinterlässt keine Lücken, also bekommt der nächste unterschiedliche Wert immer den nächsten aufeinanderfolgenden Rang"
            },
            {
              "icon": "",
              "label": "3 — DENSE_RANK() überspringt einen Rang pro Bindungsgruppe"
            }
          ]
        },
        {
          "question": "SUM(amount) OVER (PARTITION BY region ORDER BY amount) wird zu einer Query hinzugefügt. Was macht PARTITION BY region hier?",
          "options": [
            {
              "icon": "",
              "label": "Es startet die laufende Summe separat für jede Region neu, statt über das gesamte Resultset hinweg zu akkumulieren"
            },
            {
              "icon": "",
              "label": "Es filtert die Resultate zu einer einzigen Region"
            },
            {
              "icon": "",
              "label": "Es gruppiert und kollabiert die Zeilen in eine pro Region, wie GROUP BY"
            },
            {
              "icon": "",
              "label": "Es sortiert die Regionen alphabetisch bevor es summiert"
            }
          ]
        },
        {
          "question": "ROW_NUMBER() OVER (ORDER BY score DESC) wird auf fünf Zeilen angewendet, zwei davon sind genau im Score gebunden. Können zwei Zeilen je die gleiche Zeilennummer erhalten?",
          "options": [
            {
              "icon": "",
              "label": "Ja — gebundene Zeilen teilen sich immer die gleiche Zeilennummer"
            },
            {
              "icon": "",
              "label": "Nur wenn PARTITION BY auch verwendet wird"
            },
            {
              "icon": "",
              "label": "Das hängt davon ab, ob die Bindung in der ersten oder letzten Position ist"
            },
            {
              "icon": "",
              "label": "Nein — ROW_NUMBER() weist jeder Zeile immer eine eindeutige, streng steigende Ganzzahl zu, selbst wenn die Werte gebunden sind"
            }
          ]
        },
        {
          "question": "WITH high_earners AS (SELECT * FROM employees WHERE salary > 100000) SELECT department, COUNT(*) FROM high_earners GROUP BY department. Was ist high_earners?",
          "options": [
            {
              "icon": "",
              "label": "Ein Common Table Expression (CTE) — ein benanntes, temporäres Resultset, das der Rest der Query wie eine Tabelle referenzieren kann"
            },
            {
              "icon": "",
              "label": "Eine permanente Tabelle, die in der Datenbank erstellt wird"
            },
            {
              "icon": "",
              "label": "Ein View, der nach Abschluss der Query bleibt"
            },
            {
              "icon": "",
              "label": "Eine gespeicherte Prozedur, die separat aufgerufen werden muss"
            }
          ]
        },
        {
          "question": "Eine Spalte wird als PRIMARY KEY deklariert. Kannst du eine Zeile einfügen, wo diese Spalte NULL ist?",
          "options": [
            {
              "icon": "",
              "label": "Ja — PRIMARY KEY erzwingt nur Eindeutigkeit, nicht NULL-heit"
            },
            {
              "icon": "",
              "label": "Ja, aber nur eine NULL-Zeile ist erlaubt, genauso wie bei einem UNIQUE-Constraint"
            },
            {
              "icon": "",
              "label": "Nein — eine PRIMARY KEY-Spalte ist implizit NOT NULL, also wird das Einfügen von NULL darin abgelehnt"
            },
            {
              "icon": "",
              "label": "Das hängt davon ab, ob die Spalte auch einen Standardwert hat"
            }
          ]
        },
        {
          "question": "Innerhalb einer offenen Transaktion führst du ein UPDATE aus, hast aber noch keinen COMMIT ausgeführt. Von einer zweiten, separaten Verbindung zur Datenbank aus ist dieses Update sichtbar?",
          "options": [
            {
              "icon": "",
              "label": "Ja — alle Verbindungen sehen jeden Schreib sofort, wenn er läuft"
            },
            {
              "icon": "",
              "label": "Ja, aber nur wenn die zweite Verbindung auch eine Transaktion öffnet"
            },
            {
              "icon": "",
              "label": "Das hängt nur davon ab, welche Tabelle aktualisiert wurde"
            },
            {
              "icon": "",
              "label": "Nein — eine unbegebene Änderung ist nur innerhalb der Transaktion, die sie machte, sichtbar, bis COMMIT sie dauerhaft macht und für andere sichtbar"
            }
          ]
        },
        {
          "question": "products.category_id hat ein FOREIGN KEY-Constraint, das categories.id referenziert. Du versuchst eine Zeile aus categories zu LÖSCHEN, die immer noch Produkte hat, die auf sie zeigen, ohne ON DELETE-Regel angegeben. Was passiert?",
          "options": [
            {
              "icon": "",
              "label": "Die category-Zeile wird gelöscht und die zutreffenden products.category_id-Werte werden automatisch auf NULL gesetzt"
            },
            {
              "icon": "",
              "label": "Die category-Zeile wird gelöscht und jedes Produkt, das auf sie verwies, wird auch gelöscht"
            },
            {
              "icon": "",
              "label": "Das DELETE wird abgelehnt — das Standard-Fremdschlüsselverhalten blockiert das Löschen einer referenzierten Zeile, während abhängige Zeilen immer noch auf sie zeigen"
            },
            {
              "icon": "",
              "label": "Das DELETE gelingt stillschweigend, lässt die products' category_id auf eine category zeigen, die nicht mehr existiert"
            }
          ]
        }
      ],
      "optionOrderVersion": "e0466e547cfd8a45"
    }
  }
}
