{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "# Data Warehousing: Aufgaben (Lösungen)\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 1\n", "\n", "Gegeben sind die unten stehenden Anwendungsszenarien; kategorisieren Sie diese als OLAP- oder OLTP-Anfragen?\n", "\n", "1. Kassenverwaltung im Supermarkt.\n", "1. Auswirkung von Werbekampagnen auf Verkaufszahlen bestimmen.\n", "1. Ticketwebseite für Konzerte.\n", "1. Überwachung des Flugraums (Fluglotsen).\n", "1. Identifizieren der wichtigsten Kunden.\n", "1. „Wird oft zusammen gekauft“ (z.B. bei Amazon)." ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "1. Kassenverwaltung im Supermarkt. (OLTP: Verkäufe sind Transaktionen)\n", "1. Auswirkung von Werbekampagnen auf Verkaufszahlen bestimmen. (OLAP: Vergleich von Verkaufsdaten vor & nach der Kampagne)\n", "1. Ticketwebseite für Konzerte. (OLTP: Ticketverkäufe sind Transaktionen)\n", "1. Überwachung des Flugraums (Fluglotsen). (OLTP: Viele transaktionale Updates (Flugzeugpositionen), OLAP: Analytische Anfragen zur Überwachung des Flugraums) (Die Unterscheidung von OLAP und OLTP ist nicht immer eindeutig)\n", "1. Identifizieren der wichtigsten Kunden. (OLAP: Untersuchung von Verkaufsdaten)\n", "1. „Wird oft zusammen gekauft“ (z.B. bei Amazon). (OLAP: Untersuchung von Verkaufsdaten)\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 2\n", "\n", "Gegeben sei der folgende OLAP Würfel\n", "\n", "![](../resources/08_data_warehousing/olap_cube_clicks.png)\n", "\n", "Der Würfel beschreibt die Clickstream-Datenbank. Diese ist eine einfache Datenbank, welche eine Aufzeichnung der von einem Benutzer auf einer Website getätigten Klicks speichert.\n", "\n", "Eine solche Datenbank kann zum Beispiel benutzt werden für:\n", "- Marketing \n", "- Click Fraud Detection\n", "- Verbesserung des Websitedesigns\n", "\n", "\n", "\n", "Für die Modellierungen gelten die folgenden Eigenschaften:\n", "\n", "\n", "* Dimensionen: Page, User, Session\n", "* Eine Zelle beschreibt eine Zusammenfassung der Klicks, die ein bestimmter Nutzer in einer bestimmten Session auf einer bestimmten Page getätigt hat.\n", "* Beachte: Logische Repräsentation, die meisten Zellen sind leer!\n", "\n", "User haben die folgenden Attribute:\n", "- Name, Email, Alter, Adresse, Kreditkartennummer, Kreditkarteninhaber, Kreditkartenablaufdatum, \n", "\n", "Session hat die folgenden Attribute:\n", "- Sessionstart, Sessionende, IP, Browsertyp, Browserversion, Betriebssystem\n", "\n", "Page hat die folgenden Attribute:\n", "- Seitenname, Seitendomain, Seitensubdomain" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 2.1 Modellierung\n", "\n", "Im Folgenden modellieren Sie die Klicks mittels aller drei Schemata, welche Sie in diesem Modul zu Data Warehousing kennengelernt haben. \n", "\n", "#### Aufgabe 2.1.1: Sternschema\n", "\n", "Modellieren Sie die Klicks in einem Sternschema. " ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "![](../resources/08_data_warehousing/clicks_star_scheme_v1.drawio.png)\n", "\n", "Unbedingt diskutieren:\n", "\n", "+ wie viele Relationen werden benöigt\n", "+ welche Daten bzw. Attribute sollen in diesen Relationen stehen\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 2.1.2: Schneeflockenschema\n", "\n", "Modellieren Sie die Klicks in einem Schneeflockenschema. " ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "![](../resources/08_data_warehousing/clicks_snowflake_scheme_v1.drawio.png)\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 2.1.2: Fullfact-Schema\n", "\n", "Modellieren Sie die Klicks in einem Fullfact-Schema. " ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "##### Musterlösung\n", "\n", "![](../resources/08_data_warehousing/clicks_fullfact_scheme_v1.drawio.png)\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 2.2 Vergleich der Schemata\n", "\n", "Wie viele Joins benötigen wir in jedem Schema, um für alle Nutzer aus Berlin, die am 21.06.21 auf der Seite dima.tu-berlin.de waren, die Daten auszuwerten?" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "- Star 3\n", " - tabellen : facts, page, session, user \n", "- Snowflake 5\n", " - tabellen : facts, page, user, adress, city, session\n", "- Fullfact 3\n", " - tabellen : facts, page, session, city\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 2.3\n", "\n", "Was sind die Vorteile und Nachteile der jeweiligen Schemata?" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "\n", "#### Star\n", "Vorteile:\n", "- flexibel, wenige Joins\n", "- OLAP Anfragen können als einfache SQL-Anfragen modelliert werden.\n", "\n", "Nachteil:\n", "- Dimensionstabellen sind denormalisiert\n", "- Probleme bei ändernden Dimensionen\n", "\n", "#### Snowflake\n", "Vorteile:\n", "- sehr strukturiert, wenig Speicherplatz\n", "- robust gegen verändernde Dimensionen\n", "\n", "\n", "Nachteil:\n", "- viele Joins, sehr langsam\n", "\n", "\n", "#### Fullfact\n", "Vorteile:\n", "- kombiniert Vorteile von Star und Snowflake\n", "\n", "\n", "Nachteil:\n", "- sehr hoher Speicherbedarf\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 3\n", "\n", "Gegeben sei das folgende **Sternschema** zum Modellieren einer Verkaufsdatenbank aus dem Star-Schema-Benchmark.\n", "![](../resources/08_data_warehousing/ssb-schema.PNG)\n", "\n", "Dieser OLAP-Würfel hat 5 Dimensionen, die als Dimensionstabellen modelliert sind: Customer, Supplier, Part sowie Orderdate und Commitdate.\n", "\n", "Außerdem gibt es noch weitere Dimensionen, die direkt in der Faktentabelle modelliert sind, z.B. orderpriority und shippriority.\n", "\n", "Als Erstes laden wir die Beispieldatenbank:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "import duckdb\n", "\n", "con = duckdb.connect(database=\"resources/08_data_warehousing/ssb.duckdb\", read_only=False)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Zuerst testen wir die Datenbankverbindung, indem wir die Anzahl der Einträge in der Faktentabelle zählen." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "SELECT count(*)\n", "FROM lineorder\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 3.1: Drill down mittels Dicing und Slicing\n", "\n", "Die Faktentabelle enthält ca. 60000 Zeilen. Jeder Eintrag ist ein einzelner Bestellposten. \n", "\n", "(Ein Bestellposten ist Teil einer Bestellung und besteht unter anderem aus einem Produkt und der Anzahl des Produkts. Stellen Sie sich eine Rechnung eines Gemüsehändlers vor, mit 10 Äpfeln und 20 Birnen. Dann sind z.B. die 10 Äpfel ein Bestellposten, d.h., 1 Eintrag in der Faktentabelle.)\n", "\n", "Die Datenbank enthält nur Bestellungen aus dem Januar 1992.\n", "\n", "Im Folgenden führen wir ein Drill down mittels Dicing und Slicing durch.\n", "\n", "Zuerst führen wir ein Dicing durch. Wir erstellen einen OLAP-Würfel, der den Umsatz (revenue) gruppiert nach Jahr des Bestelldatums und der die Herkunftsregion der Käufer beinhaltet." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "SELECT year, region, sum(revenue)\n", "FROM lineorder l \n", "JOIN date d ON (l.orderdate = d.datekey) \n", "JOIN customer c ON (l.custkey = c.custkey)\n", "GROUP BY year, region\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Der OLAP-Würfel hat 2 **Dimensionen**: Bestelldatum und Käufer. Die Granularität der Bestelldatum-Dimension ist Jahr und die Granularität der Käufer-Dimension ist Region.\n", "\n", "**Granularität**: Granularität bezieht sich auf das Detailniveau, auf dem Daten analysiert oder gespeichert werden, und bestimmt das Maß an Spezifität oder Aggregationsgrad der Daten." ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.1.1: Dicing\n", "\n", "Erstellen Sie einen OLAP-Würel, der den Umsatz der Bestellungen gruppiert nach Jahr, Monat und Wochentag (dayofweek) und nach dem Land der Käufer gruppiert ist.\n", "\n", "- Wie viele Dimensionen hat dieser OLAP-Würel?\n", "- Was ist die Granularität der Dimensionen?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "- Der OLAP-Würfel hat weiterhin 2 Dimensionen (orderdate und customer). Die Attribute year, month, dayofweek gehören alle zur Dimension orderdate.\n", "- Die Granularität ist dayofweek und nation.\n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "query = \"\"\"\n", "SELECT year, month, dayofweek, nation, sum(revenue)\n", "FROM lineorder l \n", "JOIN date d ON (l.orderdate = d.datekey) \n", "JOIN customer c ON (l.custkey = c.custkey)\n", "GROUP BY year, month, dayofweek, nation\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.1.2: Slicing\n", "\n", "Verfeinern Sie den bestehenden OLAP-Würfel, in dem sie nur die Umsatzsummen an Dienstagen und in Europa ausgeben.\n", "\n", "- Wie viele Dimensionen hat dieser OLAP-Würfel?\n", "- Was ist die Granularität der Dimensionen?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "- Der OLAP-Würfel hat weiterhin 2 Dimensionen (orderdate und customer). Die Attribute year, month, dayofweek gehören alle zur Dimension orderdate.\n", "- Die Granularität ist nach wie vor dayofweek und nation.\n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "query = \"\"\"\n", "SELECT year, month, dayofweek, nation, sum(revenue)\n", "FROM lineorder l \n", "JOIN date d ON (l.orderdate = d.datekey) \n", "JOIN customer c ON (l.custkey = c.custkey)\n", "WHERE dayofweek = 'Tuesday'\n", "AND region = 'EUROPE'\n", "GROUP BY year, month, dayofweek, nation\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.1.3: Slicing und Dicing\n", "\n", "Verfeinern Sie den vorherigen OLAP-Würfel, in dem sie nur Einträge in Deutschland auswählen und gleichzeitig nach den Städten der Käufer gruppieren. Zählen Sie außerdem die Anzahl der Einträge in jeder Gruppe.\n", "\n", "- Wie viele Dimensionen hat dieser OLAP-Würfel?\n", "- Was ist die Granularität der Dimensionen?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "SELECT ...\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "- Der OLAP-Würfel hat weiterhin 2 Dimensionen (orderdate und customer). Die Attribute year, month, dayofweek gehören alle zur Dimension orderdate.\n", "- Die Granularität ist nun dayofweek und city.\n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "query = \"\"\"\n", "SELECT year, month, dayofweek, city, sum(revenue), count(*)\n", "FROM lineorder l \n", "JOIN date d ON (l.orderdate = d.datekey) \n", "JOIN customer c ON (l.custkey = c.custkey)\n", "WHERE dayofweek = 'Tuesday'\n", "AND NATION = 'GERMANY'\n", "GROUP BY year, month, dayofweek, city\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.1.4: Ausgabe der Werte für eine Stadt\n", "\n", "Geben Sie die Bestellposten aus, die an einem Dienstag in Deutschland in der Stadt 'GERMANY 1' (zwei Leerzeichen zwischen Germany und 1) verkauft wurden.\n", "\n", "Es sollten ausgegeben werden:\n", "\n", "- Das Datum der Bestellung (date.date).\n", "- Der Name des Käufers.\n", "- Der Name des Produkts.\n", "- Der Umsatz (revenue)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "query = \"\"\"\n", "SELECT c.name, p.name, d.date, lo.revenue\n", "FROM lineorder lo \n", "JOIN customer c ON (lo.custkey = c.custkey)\n", "JOIN part p ON (lo.partkey = p.partkey)\n", "JOIN date d ON (lo.orderdate = d.datekey)\n", "WHERE city = 'GERMANY 1' AND dayofweek = 'Tuesday'\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.1.5: Auswirkungen von Verfeinerungen auf Ergebniskardinalitäten\n", "\n", "Betrachten Sie nun die Slicing- und Dicing-Operationen, die wir in den vorherigen Aufgaben verwendet haben. Welche Auswirkungen haben Verfeinerungen auf die Kardinalität unserer Ergebnisrelation?" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "##### Musterlösung\n", "- Eine Verfeinerung einer Slicing-Operation führt generell dazu, dass die Ergebnisrelation eine geringere Kardinalität aufweist.\n", "- Eine Verfeinerung einer Dicing-Operation führt generell dazu, dass die Ergebnisrelation eine höhere Kardinalität aufweist.\n", "```\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 3.2: CUBE und ROLLUP \n", "\n", "In den folgenden Teilaufgaben wenden Sie die CUBE- und ROLLUP-Operatoren an. " ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.2.1: CUBE-Operator\n", "\n", "Bestimmen Sie die Summe des Umsatzes, gruppiert nach Wochentag und Region der Kunden. Benutzen Sie den CUBE-Operator, um über mehrere Gruppensets zu gruppieren. Sortieren Sie die Anfrage nach Wochentag und Region.\n", "\n", "- Welche Gruppensets gibt es?\n", "- Wie viele Zeilen enthält die Ausgabe, wenn es 7 Wochentage und 5 Regionen gibt?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "Gruppensets:\n", "\n", "- Wochentag und Region\n", "- Wochentag\n", "- Region\n", "- Leere Gruppe (gruppiert über alle Einträge)\n", "\n", "Es müsste $7 \\cdot 5 + 7 + 5 + 1 = 48$ Gruppen geben. In der Ausgabe gibt es aber nur 47 Gruppen. Am Sonntag gab es in Afrika keine Verkäufe\n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "query = \"\"\"\n", "SELECT dayofweek, region, sum(revenue)\n", "FROM date d JOIN lineorder lo ON d.datekey = lo.orderdate\n", " JOIN customer c USING (custkey)\n", "GROUP BY CUBE (dayofweek, region)\n", "ORDER BY dayofweek ASC, region ASC\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.2.2: CUBE-Operator nachbauen \n", "\n", "Wie kann das gleiche Ergebnis ohne CUBE-Operator erzeugt werden?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "\n", "query = \"\"\"\n", "SELECT dayofweek, region, sum(revenue)\n", "FROM date d JOIN lineorder lo ON d.datekey = lo.orderdate\n", " JOIN customer c USING (custkey)\n", "GROUP BY dayofweek, region\n", "\n", "UNION \n", "\n", "SELECT dayofweek, NULL AS region, sum(revenue)\n", "FROM date d JOIN lineorder lo ON d.datekey = lo.orderdate\n", " JOIN customer c USING (custkey)\n", "GROUP BY dayofweek\n", "\n", "UNION \n", "\n", "SELECT NULL AS dayofweek, region, sum(revenue)\n", "FROM date d JOIN lineorder lo ON d.datekey = lo.orderdate\n", " JOIN customer c USING (custkey)\n", "GROUP BY region\n", "\n", "UNION \n", "\n", "SELECT NULL AS dayofweek, NULL AS region, sum(revenue)\n", "FROM date d JOIN lineorder lo ON d.datekey = lo.orderdate\n", " JOIN customer c USING (custkey)\n", " \n", "ORDER BY dayofweek ASC, region ASC\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.2.3: ROLLUP-Operator \n", "\n", "Ermitteln Sie die Summe des Umsatz gruppiert nach Region und Land. Benutzen Sie dafür den ROLLUP-Operator.\n", "\n", "Wie viele Einträge hat das Ergebnis, wenn es 24 Länder und 5 Regionen gibt?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "\n", "Das Ergebnis hat $24 + 5 + 1 = 30$ Einträge. Dies ist ein gutes Beispiel für korrelierte Daten. \n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "\n", "query = \"\"\"\n", "SELECT region, nation, sum(revenue)\n", "FROM lineorder lo JOIN customer c USING (custkey)\n", "GROUP BY ROLLUP (region, nation) \n", "ORDER BY region ASC, nation ASC\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Aufgabe 3.2.4: ROLLUP-Operator nachbauen \n", "\n", "Wie kann die Gleiche Anfrage ohne ROLLUP-Operator erzeugt werden?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "query = \"\"\"\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "```{admonition} Musterlösung\n", ":class: dropdown, hint\n", "#### Musterlösung\n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "#### Musterlösung\n", "\n", "query = \"\"\"\n", "SELECT region, nation, sum(revenue)\n", "FROM lineorder lo\n", "JOIN customer c ON (lo.custkey = c.custkey)\n", "GROUP BY region, nation\n", "\n", "UNION \n", "\n", "SELECT region, NULL AS nation, sum(revenue)\n", "FROM lineorder lo\n", "JOIN customer c ON (lo.custkey = c.custkey)\n", "GROUP BY region\n", "\n", "UNION \n", "\n", "SELECT NULL AS region, NULL AS nation, sum(revenue)\n", "FROM lineorder lo\n", "JOIN customer c ON (lo.custkey = c.custkey)\n", "\n", "ORDER BY region ASC, nation ASC\n", "\"\"\"\n", "con.execute(query).fetchdf()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Der folgende Befehl schließt die Datenbank." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Einkommentieren, falls Sie die Verbindung nicht schließen wollen\n", "con.close()" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 4 }