<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<game format-version="2" db-system="sqlite" db-system-min-version="3.37.0">
    <head>
        <title>Baufirma</title>
        <teaser>Du bist Bauunternehmer und ein Investor stellt dir einige Fragen...

Schwierigkeitsgrad: Sehr hoch

Du brauchst NULL, Aggregatfunktionen, JOINs, Self-Joins, GROUP BY, HAVING und Unterabfragen.</teaser>
        <copyright>© 2024 Niklas J., Finn V., Lasse M., Bennet W.</copyright>
    </head>
    <scenes>
        <text-scene>
            <text>*Du leitest eine Baufirma*

„Ich bin Investor und hätte ein paar Fragen zu dieser Firma, weil ich sie kaufen möchte. Bitte beantworten Sie mir diese Fragen mit Hilfe der Datenbank Ihres Unternehmens.“</text>
        </text-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="true" are-col-names-relevant="true">
            <text>„Probier doch mal die folgende Abfrage aus: `SELECT * FROM mitarbeiter`. Sie zeigt alle Mitarbeiter mit allen Spalten.“</text>
            <sql-solution>SELECT *
FROM mitarbeiter</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie viele Bauprojekte gibt es insgesamt? Geben Sie genau eine Zahl aus.“</text>
            <sql-solution>SELECT COUNT(*) AS anzahl_projekte
FROM projekt</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Mit `COUNT(*)` zählen Sie die Zeilen der Tabelle `projekt`.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie viele Mitarbeiter arbeiten an keinem Projekt? Gemeint sind Mitarbeiter ohne Projektzuordnung. Geben Sie genau eine Zahl aus.“</text>
            <sql-solution>SELECT COUNT(*) AS anzahl_mitarbeiter
FROM mitarbeiter
WHERE projekt_id IS NULL</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Die Projektzuordnung steht in `mitarbeiter.projekt_id`. Ohne Zuordnung ist dieser Wert `NULL`.</text-hint>
                <text-hint>Filtern Sie mit `IS NULL` und zählen Sie die passenden Zeilen mit `COUNT(*)`. Ein Vergleich mit `= NULL` prüft nicht auf fehlende Werte.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie hoch ist die Summe der Projektbudgets, aufgeschlüsselt nach Standort und Status der Projekte? Geben Sie Standort, Status und Gesamtbudget in Euro aus.“</text>
            <sql-solution>SELECT standort, status, SUM(budget) AS gesamtbudget
FROM projekt
GROUP BY standort, status</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Gruppieren Sie nach beiden Merkmalen, `standort` und `status`, und summieren Sie innerhalb jeder Gruppe das `budget` mit `SUM`.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie heißt der Mitarbeiter, der am meisten verdient? Geben Sie nur die Namen aus; bei gleichem Höchstgehalt sollen alle betreffenden Mitarbeiter erscheinen. Verglichen wird das monatliche Bruttogehalt.“</text>
            <sql-solution>SELECT name
FROM mitarbeiter
WHERE gehalt = (SELECT MAX(gehalt) FROM mitarbeiter)</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Mit `MAX(gehalt)` finden Sie das höchste Gehalt. Gesucht sind aber die Namen der Mitarbeiter mit diesem Gehalt.</text-hint>
                <text-hint>Ermitteln Sie das höchste Gehalt in einer Unterabfrage und vergleichen Sie in `WHERE` jedes Gehalt damit. So bleiben bei einem Gleichstand alle betreffenden Mitarbeiter im Ergebnis.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie viel kostet das Material, das für die Projekte in Berlin bestellt wurde? Geben Sie den Gesamtwert aller erfassten Materialbestellungen für diese Projekte als einen Betrag in Euro aus.“</text>
            <sql-solution>SELECT SUM(b.einzelpreis * b.anzahl) AS bestellwert
FROM bestellung AS b
JOIN projekt AS p ON b.projekt_id = p.projekt_id
WHERE p.standort = 'Berlin'</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Der Standort steht in `projekt`, die Bestellungen stehen in `bestellung`. Über `projekt_id` können Sie die Bestellungen den Berliner Projekten zuordnen.</text-hint>
                <text-hint>Multiplizieren Sie für jede Bestellung `einzelpreis` mit `anzahl` und addieren Sie die Werte mit `SUM`. `anzahl` ist die bestellte Menge; `material.menge` bezeichnet den Lagerbestand.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie hoch ist der durchschnittliche Preis pro Einheit für jedes Material, das für das Projekt Brücke in Hamburg bestellt wurde? Jede Bestellung zählt einmal, unabhängig von ihrer Menge. Geben Sie Materialname und Durchschnittspreis in Euro aus; runden Sie auf zwei Nachkommastellen.“</text>
            <sql-solution>SELECT m.name, ROUND(AVG(b.einzelpreis), 2) AS durchschnittspreis
FROM bestellung AS b
JOIN material AS m ON b.material_id = m.material_id
JOIN projekt AS p ON b.projekt_id = p.projekt_id
WHERE p.name = 'Brücke' AND p.standort = 'Hamburg'
GROUP BY m.material_id, m.name</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Verbinden Sie `bestellung` über `material_id` mit `material` und über `projekt_id` mit `projekt`. Filtern Sie nach Projektname und Standort.</text-hint>
                <text-hint>Gruppieren Sie nach Material und verwenden Sie `AVG` für `bestellung.einzelpreis`. Jede Bestellung zählt einmal, deshalb geht ihre Menge nicht in die Berechnung ein.</text-hint>
                <text-hint>Wenden Sie `ROUND(..., 2)` auf den berechneten Durchschnitt an, damit erst das Endergebnis gerundet wird.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie viele Mitarbeiter haben ein höheres Gehalt als ihr Manager? `manager_id` verweist auf die `mitarbeiter_id` des direkten Vorgesetzten; Beruf und Projekt spielen dabei keine Rolle. Mitarbeiter ohne direkten Vorgesetzten zählen nicht mit. Geben Sie genau eine Zahl aus.“</text>
            <sql-solution>SELECT COUNT(*) AS anzahl_mitarbeiter
FROM mitarbeiter AS m
JOIN mitarbeiter AS v ON m.manager_id = v.mitarbeiter_id
WHERE m.gehalt &gt; v.gehalt</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Verwenden Sie `mitarbeiter` zweimal mit verschiedenen Aliasnamen: einmal für den Mitarbeiter, einmal für seinen direkten Vorgesetzten. Verbinden Sie dessen `mitarbeiter_id` mit der `manager_id` des Mitarbeiters.</text-hint>
                <text-hint>Filtern Sie die verbundenen Zeilen auf Mitarbeiter, deren Gehalt größer als das ihres Vorgesetzten ist, und zählen Sie die Treffer. Ein `INNER JOIN` lässt Mitarbeiter ohne zugeordneten Vorgesetzten weg.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Welches Projekt hat den höchsten Budgetanteil für Materialien? Vergleichen Sie dafür den Wert der erfassten Materialbestellungen mit dem jeweiligen Projektbudget. Geben Sie genau den Projektnamen und den Prozentwert aus, ohne den Prozentwert zu runden. Wählen Sie bei gleichem Anteil das Projekt mit der kleinsten `projekt_id`. Projekte ohne Bestellungen haben einen Anteil von 0 Prozent.“</text>
            <sql-solution>SELECT p.name,
       100.0 * COALESCE(SUM(b.einzelpreis * b.anzahl), 0) / p.budget AS prozent
FROM projekt AS p
LEFT JOIN bestellung AS b ON p.projekt_id = b.projekt_id
GROUP BY p.projekt_id, p.name, p.budget
ORDER BY prozent DESC, p.projekt_id ASC
LIMIT 1</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Summieren Sie zunächst `einzelpreis * anzahl` je Projekt. Der Prozentwert ist dieser Bestellwert mal `100.0`, geteilt durch das Budget. Multiplizieren Sie vor der Division mit `100.0`, um ganzzahlige Division zu vermeiden.</text-hint>
                <text-hint>Gehen Sie von `projekt` aus und verwenden Sie einen `LEFT JOIN`, damit Projekte ohne Bestellungen erhalten bleiben. Mit `COALESCE(..., 0)` ersetzen Sie eine fehlende Summe durch null Euro.</text-hint>
                <text-hint>Sortieren Sie zuerst nach dem Prozentwert absteigend, dann nach `projekt_id` aufsteigend. Mit `LIMIT 1` wählen Sie das gesuchte Projekt aus.</text-hint>
            </hints>
        </select-scene>
        <select-scene is-row-order-relevant="false" is-col-order-relevant="false" are-col-names-relevant="false">
            <text>„Wie viele Projekte haben mehr als zwei Mitarbeiter mit demselben Beruf? Zählen Sie jedes Projekt nur einmal, auch wenn mehrere Berufe die Bedingung erfüllen. Mitarbeiter ohne Projektzuordnung gehören zu keinem Projekt. Geben Sie genau eine Zahl aus, auch wenn kein Projekt die Bedingung erfüllt.“</text>
            <sql-solution>SELECT COUNT(DISTINCT projekt_id) AS anzahl_projekte
FROM (
    SELECT projekt_id, beruf
    FROM mitarbeiter
    WHERE projekt_id IS NOT NULL
    GROUP BY projekt_id, beruf
    HAVING COUNT(*) &gt; 2
) AS passende_gruppen</sql-solution>
            <sql-placeholder></sql-placeholder>
            <hints>
                <expected-result-hint />
                <text-hint>Gruppieren Sie die zugeordneten Mitarbeiter nach `projekt_id` und `beruf`. Mit `HAVING COUNT(*) &gt; 2` finden Sie die passenden Gruppen.</text-hint>
                <text-hint>Verwenden Sie diese Gruppen als Unterabfrage. Ein äußeres `COUNT(DISTINCT projekt_id)` zählt jedes Projekt nur einmal und liefert auch bei keiner passenden Gruppe eine Zahl.</text-hint>
            </hints>
        </select-scene>
        <text-scene>
            <text>„Vielen Dank für Ihre Hilfe!!! Ihre Auswertung gibt mir einen ersten Überblick über die Firma.“

Herzlichen Glückwunsch! Du hast gewonnen!</text>
        </text-scene>
    </scenes>
    <initial-sql-script>-- Fiktiver Datenauszug: Budgets und Preise in Euro, Gehälter brutto pro Monat.
-- Bestellungen bilden nur einen Teil der Materialbeschaffung ab.
CREATE TABLE projekt (
    projekt_id INTEGER PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    standort VARCHAR(50) NOT NULL,
    budget DECIMAL(10,2) NOT NULL CHECK (budget &gt; 0),
    status VARCHAR(20) NOT NULL
);

CREATE TABLE mitarbeiter (
    mitarbeiter_id INTEGER PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    gehalt DECIMAL(10,2) NOT NULL CHECK (gehalt &gt;= 0),
    beruf VARCHAR(30) NOT NULL,
    projekt_id INTEGER REFERENCES projekt (projekt_id),
    manager_id INTEGER REFERENCES mitarbeiter (mitarbeiter_id),
    CHECK (manager_id IS NULL OR manager_id &lt;&gt; mitarbeiter_id)
);

-- menge bezeichnet den Lagerbestand in der angegebenen Einheit.
CREATE TABLE material (
    material_id INTEGER PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    einheit VARCHAR(20) NOT NULL,
    menge INTEGER NOT NULL CHECK (menge &gt;= 0)
);

-- Preise werden je Bestellung festgehalten, nicht im Materialstamm.
CREATE TABLE bestellung (
    bestellung_id INTEGER PRIMARY KEY,
    datum DATE NOT NULL,
    projekt_id INTEGER NOT NULL REFERENCES projekt (projekt_id),
    material_id INTEGER NOT NULL REFERENCES material (material_id),
    anzahl INTEGER NOT NULL CHECK (anzahl &gt; 0),
    einzelpreis DECIMAL(10,2) NOT NULL CHECK (einzelpreis &gt;= 0)
);

INSERT INTO projekt VALUES
(1, 'Wohnhaus', 'Berlin', 500000.00, 'in Bau'),
(2, 'Brücke', 'Hamburg', 1000000.00, 'geplant'),
(3, 'Schule', 'München', 800000.00, 'fertig'),
(4, 'Bürogebäude', 'Berlin', 750000.00, 'in Bau'),
(5, 'Sporthalle', 'Berlin', 400000.00, 'geplant'),
(6, 'Kita', 'Hamburg', 300000.00, 'geplant'),
(7, 'Werkstatt', 'München', 200000.00, 'fertig');

-- Vorgesetzte werden ausdrücklich zugeordnet, auch projektübergreifend.
INSERT INTO mitarbeiter VALUES
(1, 'Anna Müller', 4800.00, 'Architektur', 1, 6),
(2, 'Bernd Schmidt', 5200.00, 'Bauingenieurwesen', 2, 6),
(3, 'Carl Weber', 3500.00, 'Maurerhandwerk', 1, 1),
(4, 'Doris Klein', 2800.00, 'Verwaltung', NULL, 6),
(5, 'Erik Wolf', 3900.00, 'Elektroinstallation', 3, 6),
(6, 'Farah Neumann', 5000.00, 'Projektmanagement', 4, 7),
(7, 'Greta Hoffmann', 7000.00, 'Geschäftsführung', NULL, NULL),
(8, 'Hannes Braun', 4800.00, 'Maurerhandwerk', 1, 1),
(9, 'Ines Richter', 5100.00, 'Maurerhandwerk', 1, 1),
(10, 'Jonas Koch', 3200.00, 'Elektroinstallation', 1, 1),
(11, 'Karin Lange', 4800.00, 'Elektroinstallation', 1, 1),
(12, 'Lars Schulz', 4900.00, 'Elektroinstallation', 1, 1),
(13, 'Mina Becker', 4100.00, 'Maurerhandwerk', 2, 2),
(14, 'Nils Fischer', 5300.00, 'Maurerhandwerk', 2, 2),
(15, 'Oskar Schmitt', 3800.00, 'Elektroinstallation', 3, 5),
(16, 'Pia Wagner', 3900.00, 'Elektroinstallation', 3, 5),
(17, 'Quirin Roth', 3000.00, 'Verwaltung', NULL, 6),
(18, 'Rita Wolf', 2900.00, 'Verwaltung', NULL, 6);

INSERT INTO material VALUES
(1, 'Zement', 'Sack (25 kg)', 1000),
(2, 'Stahl', 'kg', 500),
(3, 'Holz', 'Brett', 800),
(4, 'Kabel', 'm', 1200),
(5, 'Farbe', 'Liter', 600);

INSERT INTO bestellung VALUES
(1, '2023-11-01', 1, 1, 100, 5.00),
(2, '2023-11-02', 1, 2, 50, 10.00),
(3, '2023-11-03', 2, 2, 100, 10.00),
(4, '2023-11-04', 3, 3, 200, 8.00),
(5, '2023-11-05', 3, 4, 300, 3.00),
(6, '2023-11-06', 2, 2, 300, 14.00),
(7, '2023-11-07', 2, 1, 100, 6.00),
(8, '2023-11-08', 2, 1, 50, 10.00),
(9, '2023-11-09', 4, 3, 100, 9.00),
(10, '2023-11-10', 4, 5, 50, 6.00),
(11, '2023-11-11', 5, 2, 20, 10.00),
(12, '2023-11-12', 6, 2, 110, 21.00),
(13, '2023-11-13', 2, 2, 100, 14.00);</initial-sql-script>
</game>
