Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
| modul:m290_guko:learningunits:lu10:theorie:a_einfuehrung [2025/11/05 18:26] – gkoch | modul:m290_guko:learningunits:lu10:theorie:a_einfuehrung [2025/11/06 00:18] (aktuell) – gkoch | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| - | ====== LU10: Aggregatfunktionen | + | ====== LU10: Aggregatfunktionen ====== |
| **Datensatz**: | **Datensatz**: | ||
| Zeile 24: | Zeile 24: | ||
| === Wozu braucht man das? === | === Wozu braucht man das? === | ||
| - | * **Datenanalyse & Reporting** (z.B. Lageberichte | + | * **Datenanalyse & Reporting** – Beispiel (Einbrüche ZH): Wie viele Fälle gab es pro Jahr? |
| - | * **Business Intelligence** (Trends, Hotspots je Gemeinde/ | + | * **Nutzen:** Klare Jahreskurven für **Berichte**, |
| - | * **Produkt-/ | + | * **Business Intelligence |
| - | * **Entscheidungsgrundlagen** (Ressourcen planen, Präventionsmassnahmen priorisieren) | + | * **Nutzen:** **Prioritäten** setzen (Präventionskampagnen, Ressourceneinsatz, |
| + | * **Produkt-/ | ||
| + | * **Nutzen:** Das **Backend** liefert fertige Kennzahlen; die **Frontend-Logik** bleibt schlank und schnell. | ||
| Zeile 47: | Zeile 49: | ||
| </ | </ | ||
| - | ==== 1.2 Einstiegsbeispiele | + | ==== 1.2 Einstiegsbeispiele ==== |
| === Anzahl unterschiedlicher Gemeinden im Datensatz === | === Anzahl unterschiedlicher Gemeinden im Datensatz === | ||
| Zeile 70: | Zeile 72: | ||
| ===== 2. GROUP BY – Daten zu Gruppen zusammenfassen ===== | ===== 2. GROUP BY – Daten zu Gruppen zusammenfassen ===== | ||
| + | |||
| + | {{ : | ||
| <WRAP round 80% box center> | <WRAP round 80% box center> | ||
| Zeile 77: | Zeile 81: | ||
| </ | </ | ||
| - | 1) Was darf in SELECT stehen? | + | === Was darf in SELECT stehen? |
| Nur Aggregatfunktionen (z.B. SUM(...), AVG(...)) oder Spalten, die im GROUP BY aufgeführt sind. | Nur Aggregatfunktionen (z.B. SUM(...), AVG(...)) oder Spalten, die im GROUP BY aufgeführt sind. | ||
| - | Gültig: | + | Gültig: |
| + | <code sql> | ||
| + | SELECT ausgangsjahr, | ||
| + | FROM zh_einbrueche.einbrueche | ||
| + | GROUP BY ausgangsjahr; | ||
| + | </ | ||
| - | Ungültig (fehlendes GROUP BY für gemeindename): | + | Ungültig (fehlendes GROUP BY für gemeindename): |
| + | <code sql> | ||
| + | SELECT gemeindename, | ||
| + | FROM zh_einbrueche.einbrueche; | ||
| + | </ | ||
| + | |||
| + | === WHERE vs. HAVING === | ||
| + | * **WHERE** filtert einzelne Zeilen vor dem Gruppieren. | ||
| + | * **HAVING** filtert fertige Gruppen nach dem Gruppieren. | ||
| - | 2) WHERE vs. HAVING | ||
| - | WHERE filtert einzelne Zeilen vor dem Gruppieren. | ||
| - | HAVING filtert fertige Gruppen nach dem Gruppieren. | ||
| <WRAP round 80% box center> | <WRAP round 80% box center> | ||
| Beispiel: Nur Jahre ab 2018 berücksichtigen (WHERE), und nur Gruppen mit Total > 1'000 zeigen (HAVING) | Beispiel: Nur Jahre ab 2018 berücksichtigen (WHERE), und nur Gruppen mit Total > 1'000 zeigen (HAVING) | ||
| <code sql> | <code sql> | ||
| - | SELECT ausgangsjahr, | + | SELECT ausgangsjahr, |
| + | FROM zh_einbrueche.einbrueche | ||
| + | WHERE ausgangsjahr >= 2018 -- Zeilenfilter | ||
| + | GROUP BY ausgangsjahr | ||
| + | HAVING SUM(straftaten_total) > 15000; -- Gruppenfilter | ||
| </ | </ | ||
| + | //Ergebnis: In den Jahren 2018, 2019 und 2024 haben über 15000 Einbrüche total stattgefunden.// | ||
| </ | </ | ||
| - | |||
| - | 3) Alias in GROUP BY (MySQL erlaubt es, Standard nicht) | ||
| - | MySQL: Alias kann verwendet werden. Für Portabilität (z.B. andere SQL‑Dialekte): | ||
| ==== 2.1 Beispiele: Gruppieren nach einem Kriterium ==== | ==== 2.1 Beispiele: Gruppieren nach einem Kriterium ==== | ||
| <WRAP round 80% box center> | <WRAP round 80% box center> | ||
| - | **a) Total Einbrüche pro Jahr** <code sql> | + | **Total Einbrüche pro Jahr** <code sql> |
| SELECT ausgangsjahr, | SELECT ausgangsjahr, | ||
| SUM(straftaten_total) AS total_faelle | SUM(straftaten_total) AS total_faelle | ||
| - | FROM | + | FROM zh_einbrueche.einbrueche |
| - | GROUP BY ausgangsjahr | + | GROUP BY ausgangsjahr |
| - | ORDER BY ausgangsjahr; | + | ORDER BY ausgangsjahr; |
| **Ergebnis (Auszug)** | **Ergebnis (Auszug)** | ||
| ^ ausgangsjahr ^ total_faelle ^ | ^ ausgangsjahr ^ total_faelle ^ | ||
| - | | 2019 | 5080 | | + | | 2009 | 29972 | |
| - | | 2020 | 4525 | | + | | 2010 | 26770 | |
| - | | 2021 | 4789 | | + | | 2011 | 24394 | |
| + | ... etc. | ||
| </ | </ | ||
| - | <WRAP round 80% box center> | ||
| - | **b) Anzahl erfasste Gemeinden pro Jahr** | ||
| - | <code sql> | ||
| - | SELECT ausgangsjahr, | ||
| - | COUNT(DISTINCT gemeindename) AS anzahl_gemeinden | ||
| - | FROM | ||
| - | GROUP BY ausgangsjahr | ||
| - | ORDER BY ausgangsjahr; | ||
| - | **Ergebnis (Auszug)** | ||
| - | ^ ausgangsjahr ^ anzahl_gemeinden ^ | ||
| - | | 2019 | 171 | | ||
| - | | 2020 | 171 | | ||
| - | | 2021 | 171 | | ||
| - | </ | ||
| ==== 2.2 Beispiele: Gruppieren nach mehreren Kriterien ==== | ==== 2.2 Beispiele: Gruppieren nach mehreren Kriterien ==== | ||
| Zeile 137: | Zeile 140: | ||
| tatbestand, | tatbestand, | ||
| SUM(straftaten_total) AS total_faelle | SUM(straftaten_total) AS total_faelle | ||
| - | FROM | + | FROM zh_einbrueche.einbrueche |
| - | GROUP BY ausgangsjahr, | + | GROUP BY ausgangsjahr, |
| - | ORDER BY ausgangsjahr, | + | ORDER BY ausgangsjahr, |
| **Ergebnis (Auszug)** | **Ergebnis (Auszug)** | ||
| ^ ausgangsjahr ^ tatbestand ^ total_faelle ^ | ^ ausgangsjahr ^ tatbestand ^ total_faelle ^ | ||
| - | | 2020 | Einbruch | + | | 2023 | Einschleichdiebstahl |
| - | | 2020 | Einschleichen | + | | 2024 | Einbruchdiebstahl |
| - | | 2021 | Einbruch | + | | 2024 | Einbrüche insgesamt |
| + | ... etc. | ||
| </ | </ | ||
| - | ===== 3. HAVING – Gruppen | + | ===== 3. HAVING – Gruppen filtern ===== |
| WHERE kann **keine** Aggregatfunktionen enthalten. Wenn Sie **Ergebnisgruppen** (nach GROUP BY) filtern wollen, verwenden Sie **HAVING**. | WHERE kann **keine** Aggregatfunktionen enthalten. Wenn Sie **Ergebnisgruppen** (nach GROUP BY) filtern wollen, verwenden Sie **HAVING**. | ||
| <WRAP round 80% box center> | <WRAP round 80% box center> | ||
| - | **Jahre mit über 5'000 Fällen insgesamt** <code sql> | + | **Jahre mit über 25'000 Fällen insgesamt** <code sql> |
| SELECT ausgangsjahr, | SELECT ausgangsjahr, | ||
| SUM(straftaten_total) AS total_faelle | SUM(straftaten_total) AS total_faelle | ||
| - | FROM | + | FROM zh_einbrueche.einbrueche |
| - | GROUP BY ausgangsjahr | + | GROUP BY ausgangsjahr |
| - | HAVING SUM(straftaten_total) > 5000 | + | HAVING SUM(straftaten_total) > 25000 |
| - | ORDER BY total_faelle DESC; </ | + | ORDER BY total_faelle DESC; </ |
| **Ergebnis (Auszug)** | **Ergebnis (Auszug)** | ||
| ^ ausgangsjahr ^ total_faelle ^ | ^ ausgangsjahr ^ total_faelle ^ | ||
| - | | 2013 | 6240 | | + | | 2009 | 29972 | |
| - | | 2012 | 6115 | | + | | 2012 | 28730 | |
| + | | 2010 | 26770 | | ||
| </ | </ | ||
| - | <WRAP round 80% box center> | ||
| - | **Abarbeitungsreihenfolge (vereinfacht)** | ||
| - | FROM → WHERE → GROUP BY → **Aggregatfunktionen** → HAVING → SELECT → ORDER BY. | ||
| - | </ | ||
| - | |||
| - | ===== 4. Häufige Stolpersteine (und wie man sie vermeidet) ===== | ||
| - | |||
| - | * SELECT enthält **ungegruppte Spalten** → Fehlermeldung oder Zufallswerte. | ||
| - | → Nur Spalten in GROUP BY **oder** in Aggregaten verwenden. | ||
| - | * **Division durch 0** bei Anteilen/ | ||
| - | → NULLIF(denominator, | ||
| - | * **MySQL‑Spezialität**: | ||
| - | → Ausdruck wiederholen, | ||
| - | * **NULLs**: COUNT(spalte) ignoriert NULL → **bewusste Spaltenwahl!** | ||
| - | |||
| - | ====== LU10: Aggregatfunktionen (MySQL) – mit Beispielen aus «Einbrüche Kanton Zürich» ====== | ||
| - | |||
| - | **Datensatz**: | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **Tabellen- & Feldübersicht (vereinfacht)** | ||
| - | **Datenbank**: | ||
| - | |||
| - | ^ Feld ^ Typ (MySQL) ^ Pflicht ^ Bedeutung ^ | ||
| - | | id | BIGINT UNSIGNED | ja (PK) | künstlicher Primärschlüssel | | ||
| - | | ausgangsjahr | YEAR | ja | Berichtsjahr der PKS (nicht Tatzeitpunkt) | | ||
| - | | gemeinde_bfs_nr | INT | ja | BFS-Nummer der Gemeinde | | ||
| - | | gemeindename | VARCHAR(100) | ja | Gemeindename | | ||
| - | | stadtkreis_bfs_nr | INT | nein | Nur Stadt Zürich: BFS-Nr. des Stadtkreises | | ||
| - | | stadtkreis_name | VARCHAR(100) | nein | Nur Stadt Zürich: Name des Stadtkreises | | ||
| - | | gesetz_nummer | DECIMAL(6, | ||
| - | | gesetz_abk | VARCHAR(16) | ja | Gesetzesabkürzung (z.B. StGB) | | ||
| - | | tatbestand | VARCHAR(100) | ja | z.B. Einbruch, Einschleichen | | ||
| - | | straftaten_total | INT | ja | Vollendet **+** Versucht | | ||
| - | | straftaten_vollendet | INT | ja | Vollendete Taten | | ||
| - | | straftaten_versucht | INT | ja | Versuch | | ||
| - | | einwohner | INT | ja | Bevölkerungszahl (Ende Vorjahr) | | ||
| - | | haeufigkeitszahl | DECIMAL(10, | ||
| - | </ | ||
| - | |||
| - | ===== 1. Aggregatfunktionen – Theorie & Motivation ===== | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **Was sind Aggregatfunktionen? | ||
| - | Aggregatfunktionen berechnen **einen zusammengefassten Wert** über mehrere Zeilen (Records). Typische Aufgaben: | ||
| - | |||
| - | * **Zählen** (Wie viele Einbrüche? | ||
| - | * **Summieren** (Wie viele Fälle insgesamt?) | ||
| - | * **Durchschnitt** (Ø‑Häufigkeitszahl pro Jahr) | ||
| - | * **Minimum/ | ||
| - | |||
| - | **Wozu braucht man das?** | ||
| - | |||
| - | * **Datenanalyse & Reporting** (z.B. Lageberichte für Polizei/ | ||
| - | * **Business Intelligence** (Trends, Hotspots je Gemeinde/ | ||
| - | * **Produkt-/ | ||
| - | * **Entscheidungsgrundlagen** (Ressourcen planen, Präventionsmassnahmen priorisieren) | ||
| - | |||
| - | </ | ||
| - | |||
| - | ==== 1.1 Wichtigste Funktionen (NULL-Verhalten) ==== | ||
| - | |||
| - | ^ Funktion ^ Zweck ^ NULLs ^ Beispiel ^ | ||
| - | | COUNT(*) | Zeilen zählen | zählt **alle** Zeilen | Anzahl Zeilen im Jahr | | ||
| - | | COUNT(spalte) | Nicht‑NULL‑Werte zählen | **ignoriert** NULL | z.B. vorhandene Werte in einer Spalte | | ||
| - | | SUM(spalte) | Summe | ignoriert NULL | Total aller straftaten_total | | ||
| - | | AVG(spalte) | Durchschnitt | ignoriert NULL | Ø haeufigkeitszahl pro Jahr | | ||
| - | | MIN(spalte) | Minimum | ignoriert NULL | tiefste straftaten_total pro Gemeinde | | ||
| - | | MAX(spalte) | Maximum | ignoriert NULL | höchste straftaten_total pro Gemeinde | | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **Syntax** <code sql> | ||
| - | FROM zh_einbrueche.einbrueche;</ | ||
| - | **MySQL‑Standard** ist ALL (= Duplikate werden mitgerechnet). DISTINCT rechnet nur mit **einzigartigen** Werten. | ||
| - | </ | ||
| - | |||
| - | ==== 1.2 Einstiegsbeispiele (ohne GROUP BY) ==== | ||
| - | |||
| - | **Anzahl unterschiedlicher Gemeinden im Datensatz** <code sql> | ||
| - | SELECT COUNT(DISTINCT gemeindename) AS anzahl_gemeinden | ||
| - | FROM zh_einbrueche.einbrueche; | ||
| - | **Ergebnis (Auszug)** | ||
| - | ^ anzahl_gemeinden ^ | ||
| - | | 171 | | ||
| - | |||
| - | **Höchster Einzelwert pro Zeile (Totalfälle)** <code sql> | ||
| - | SELECT MAX(straftaten_total) AS max_faelle_in_einer_zeile | ||
| - | FROM zh_einbrueche.einbrueche; | ||
| - | **Ergebnis (Auszug)** | ||
| - | ^ max_faelle_in_einer_zeile ^ | ||
| - | | 420 | | ||
| - | |||
| - | |||
| - | ===== 2. GROUP BY – Daten zu Gruppen zusammenfassen ===== | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **Warum ist GROUP BY wichtig?** | ||
| - | Ohne GROUP BY erhalten Sie **einen** Aggregatwert über **alle** Zeilen. Mit GROUP BY erhalten Sie **je Gruppe eine Zeile** (z.B. pro Jahr, pro Gemeinde oder pro Kombination aus Jahr+Gemeinde). Das ist zentral für **Trends, Rankings und Vergleiche**. | ||
| - | </ | ||
| - | |||
| - | **Regeln (MySQL‑kompatibel, | ||
| - | |||
| - | * In SELECT dürfen neben Aggregaten **nur** Spalten stehen, die auch im GROUP BY vorkommen. | ||
| - | * WHERE filtert **Zeilen vor** dem Gruppieren. HAVING filtert **Gruppen nach** dem Gruppieren. | ||
| - | * MySQL erlaubt teils Aliasse in GROUP BY (z.B. GROUP BY jahr nach SELECT YEAR(...) AS jahr). Für bessere Portabilität: | ||
| - | |||
| - | ==== 2.1 Beispiele: Gruppieren nach einem Kriterium ==== | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **a) Total Einbrüche pro Jahr** <code sql> | ||
| - | SELECT ausgangsjahr, | ||
| - | SUM(straftaten_total) AS total_faelle | ||
| - | FROM | ||
| - | GROUP BY ausgangsjahr | ||
| - | ORDER BY ausgangsjahr; | ||
| - | **Ergebnis (Auszug)** | ||
| - | ^ ausgangsjahr ^ total_faelle ^ | ||
| - | | 2019 | 5080 | | ||
| - | | 2020 | 4525 | | ||
| - | | 2021 | 4789 | | ||
| - | </ | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **b) Anzahl erfasste Gemeinden pro Jahr** <code sql> | ||
| - | SELECT ausgangsjahr, | ||
| - | COUNT(DISTINCT gemeindename) AS anzahl_gemeinden | ||
| - | FROM | ||
| - | GROUP BY ausgangsjahr | ||
| - | ORDER BY ausgangsjahr; | ||
| - | **Ergebnis (Auszug)** | ||
| - | ^ ausgangsjahr ^ anzahl_gemeinden ^ | ||
| - | | 2019 | 171 | | ||
| - | | 2020 | 171 | | ||
| - | | 2021 | 171 | | ||
| - | </ | ||
| - | |||
| - | ==== 2.2 Beispiele: Gruppieren nach mehreren Kriterien ==== | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **Total pro Jahr und Tatbestand (z.B. Einbruch, Einschleichen)** <code sql> | ||
| - | SELECT ausgangsjahr, | ||
| - | tatbestand, | ||
| - | SUM(straftaten_total) AS total_faelle | ||
| - | FROM | ||
| - | GROUP BY ausgangsjahr, | ||
| - | ORDER BY ausgangsjahr, | ||
| - | **Ergebnis (Auszug)** | ||
| - | ^ ausgangsjahr ^ tatbestand ^ total_faelle ^ | ||
| - | | 2020 | Einbruch | 3100 | | ||
| - | | 2020 | Einschleichen | 1425 | | ||
| - | | 2021 | Einbruch | 3250 | </ | ||
| - | |||
| - | ===== 3. HAVING – Gruppen gezielt filtern ===== | ||
| - | |||
| - | WHERE kann **keine** Aggregatfunktionen enthalten. Wenn Sie **Ergebnisgruppen** (nach GROUP BY) filtern wollen, verwenden Sie **HAVING**. | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **Jahre mit über 5'000 Fällen insgesamt** <code sql> | ||
| - | SELECT ausgangsjahr, | ||
| - | SUM(straftaten_total) AS total_faelle | ||
| - | FROM | ||
| - | GROUP BY ausgangsjahr | ||
| - | HAVING SUM(straftaten_total) > 5000 | ||
| - | ORDER BY total_faelle DESC; </ | ||
| - | **Ergebnis (Auszug)** | ||
| - | ^ ausgangsjahr ^ total_faelle ^ | ||
| - | | 2013 | 6240 | | ||
| - | | 2012 | 6115 | </ | ||
| - | |||
| - | <WRAP round 80% box center> | ||
| - | **Abarbeitungsreihenfolge (vereinfacht)** | ||
| - | FROM → WHERE → GROUP BY → **Aggregatfunktionen** → HAVING → SELECT → ORDER BY. | ||
| - | </ | ||
| - | |||
| - | |||
| - | ===== 4. Häufige Stolpersteine (und wie man sie vermeidet) ===== | ||
| - | * SELECT enthält **ungegruppte Spalten** → Fehlermeldung oder Zufallswerte. | ||
| - | → Nur Spalten in GROUP BY **oder** in Aggregaten verwenden. | ||
| - | * **Division durch 0** bei Anteilen/ | ||
| - | → NULLIF(denominator, | ||
| - | * **MySQL‑Spezialität**: | ||
| - | → Ausdruck wiederholen, | ||
| - | * **NULLs**: COUNT(spalte) ignoriert NULL → **bewusste Spaltenwahl!** | ||