5. Unterrichtsblock

Python und Datenbanken
In Block 4 hast du Noten in einer Textdatei gespeichert. Das klappt für kleine Listen. Sobald viele Daten zusammenkommen, die man durchsuchen, sortieren und ändern will, nimmt man eine Datenbank. Die Daten stehen dort in Tabellen, und man arbeitet mit ihnen über die Sprache SQL (Structured Query Language).
| Begriff | Bedeutung | Beispiel |
|---|---|---|
| Datenbank | eine Sammlung zusammengehöriger Tabellen, bei SQLite eine einzige Datei | schule.db |
| Tabelle | Daten zu einer Sache, aufgebaut aus Spalten und Zeilen | schueler |
| Spalte | eine Eigenschaft mit festem Namen und Datentyp | vorname (Text) |
| Zeile / Datensatz | ein einzelner Eintrag | Lina Berger, 2008, 10a |
| Primärschlüssel | Spalte, deren Wert jede Zeile eindeutig macht | id = 1, 2, 3 … |
| SQL | die Sprache, in der man mit der Datenbank spricht | CREATE TABLE … |
SQLite oder MySQL?
Wir arbeiten mit SQLite. Das Modul sqlite3 gehört zu Python, du musst nichts installieren und brauchst kein Internet. Viele Firmen und Webseiten verwenden Datenbank-Server wie MySQL. Die SQL-Befehle sind dort fast genauso, du kannst dein Wissen also mitnehmen.
| SQLite | MySQL | |
|---|---|---|
| Wo liegen die Daten? | in einer Datei neben deinem Programm | auf einem Datenbank-Server |
| Installation | keine, sqlite3 ist in Python enthalten | Server und Python-Paket nötig |
| Anmeldung | keine | Benutzer und Passwort |
| Platzhalter in Befehlen | ? | %s |
| Einsatz | Apps, Lernen, kleine bis mittlere Programme | Webseiten und Firmen mit vielen Nutzern gleichzeitig |
Verbindung und Cursor
Jede Arbeit mit einer Datenbank läuft in vier Schritten ab: verbinden, Befehle über den Cursor ausführen, Änderungen speichern, Verbindung schließen. sqlite3.connect() öffnet die Datenbankdatei und legt sie an, wenn es sie noch nicht gibt. Den Pfad baust du wie in Block 4 vom Skript-Ordner aus:
import sqlite3
from pathlib import Path
ORDNER = Path(__file__).resolve().parent
datenbank = ORDNER / "schule.db"
verbindung = sqlite3.connect(datenbank) # 1. verbinden
cursor = verbindung.cursor() # 2. Cursor holen
# cursor.execute("SQL-Befehl") # Befehle ausführen
verbindung.commit() # 3. speichern
verbindung.close() # 4. schließen
print("Datei vorhanden:", datenbank.exists())
Datei vorhanden: True
Der Cursor ist wie ein Zeiger in der Datenbank: Über ihn schickst du mit execute() SQL-Befehle und holst dir Ergebnisse ab.
verbindung.commit() dauerhaft gespeichert. Mit verbindung.close() gibst du die Datei wieder frei. Solange sie offen ist, sperrt Windows sie für andere Programme.Aufgabe 1 – Die erste Datenbank
Zeit: ca. 8 Minuten
Eine SQLite-Datenbank ist eine ganz normale Datei. Das kannst du nachmessen. Lege eine neue Datei block_05_sql_grundlagen.py an, die so beginnt:
import sqlite3
from pathlib import Path
ORDNER = Path(__file__).resolve().parent
- Verbinde dich mit
ORDNER / "test.db"und schließe die Verbindung gleich wieder. Sage vorher voraus: Gibt es die Datei danach? Wie groß ist sie (stat().st_sizeaus Block 4)? - Lege mit
CREATE TABLE IF NOT EXISTS notiz (id INTEGER PRIMARY KEY, text TEXT)eine Tabelle an, danncommit()undclose(). Wie groß ist die Datei jetzt? - Öffne
test.dbim Editor von VS Code. Warum kannst du den Inhalt nicht wie eine Textdatei lesen? Antworte als Kommentar.
Kontrolle: Erst 0 Bytes, nach dem Anlegen der Tabelle 8192 Bytes.
Tabellen erstellen
Eine Tabelle legst du mit CREATE TABLE an. Für jede Spalte gibst du Name und Datentyp an. SQLite kennt nur wenige Datentypen:
| SQLite-Typ | speichert | in MySQL etwa |
|---|---|---|
INTEGER | ganze Zahlen | INT |
REAL | Kommazahlen | FLOAT, DOUBLE |
TEXT | Text beliebiger Länge | VARCHAR(255), TEXT |
BLOB | Binärdaten, z. B. Bilder | BLOB |
NULL | „kein Wert“ (leeres Feld) | NULL |
Hinter dem Datentyp kannst du Regeln für die Spalte festlegen:
| Zusatz | Wirkung |
|---|---|
PRIMARY KEY | Primärschlüssel: eindeutig und nie leer |
AUTOINCREMENT | vergibt die Nummer automatisch: 1, 2, 3 … (MySQL: AUTO_INCREMENT) |
NOT NULL | Pflichtfeld, darf nicht leer bleiben |
UNIQUE | jeder Wert darf nur einmal vorkommen |
DEFAULT wert | Standardwert, wenn nichts angegeben wird |
Längere SQL-Befehle schreibst du in einen String mit drei Anführungszeichen, dann bleiben sie übersichtlich. IF NOT EXISTS legt die Tabelle nur an, wenn es sie noch nicht gibt:
verbindung = sqlite3.connect(datenbank)
cursor = verbindung.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS schueler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
vorname TEXT NOT NULL,
nachname TEXT NOT NULL,
geburtsjahr INTEGER,
klasse TEXT DEFAULT '10a'
)
""")
verbindung.commit()
verbindung.close()
print("Tabelle schueler angelegt")
Tabelle schueler angelegt
CREATE TABLE schreibt man üblicherweise groß, Tabellen- und Spaltennamen klein, ohne Leerzeichen und ohne Umlaute (schueler, nicht Schüler). Texte innerhalb von SQL stehen in einfachen Anführungszeichen: '10a'.Aufgabe 2 – Der Bauplan
Zeit: ca. 10 Minuten
Die Schulbibliothek braucht eine Tabelle buch in bibliothek.db. Jedes Buch hat eine automatisch vergebene Nummer, einen Titel und einen Autor (beide Pflicht), ein Erscheinungsjahr, eine ISBN, die es nur einmal geben darf, und einen Status ausgeliehen, der am Anfang 0 ist.
- Schreibe den
CREATE TABLE-Befehl und führe ihn aus. - Gib mit
PRAGMA table_info(buch)alle Spalten aus. Kontrolle: 6 Spalten. - Lass
IF NOT EXISTSweg und starte das Programm zweimal. Sage vorher voraus, was beim zweiten Start passiert, und notiere die Fehlermeldung als Kommentar.
Tabellen anzeigen und prüfen
Welche Tabellen in einer Datenbank stecken, steht in der internen Tabelle sqlite_master. Die Spalten einer Tabelle liefert PRAGMA table_info(name). Das Ergebnis holst du mit fetchall() ab, eine Liste mit einem Tupel pro Zeile (mehr dazu in Block 6):
verbindung = sqlite3.connect(datenbank)
cursor = verbindung.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table'")
print(cursor.fetchall())
cursor.execute("PRAGMA table_info(schueler)")
for spalte in cursor.fetchall():
print(spalte)
verbindung.close()
[('schueler',), ('sqlite_sequence',)]
(0, 'id', 'INTEGER', 0, None, 1)
(1, 'vorname', 'TEXT', 1, None, 0)
(2, 'nachname', 'TEXT', 1, None, 0)
(3, 'geburtsjahr', 'INTEGER', 0, None, 0)
(4, 'klasse', 'TEXT', 0, "'10a'", 0)Neben schueler taucht sqlite_sequence auf: Dort merkt sich SQLite, welche Nummer AUTOINCREMENT als Nächstes vergibt. Jede Spaltenzeile enthält Nummer, Name, Datentyp, Pflicht (1 = NOT NULL), Standardwert und Primärschlüssel (1 = ja).
Ob es die Datenbank selbst gibt, prüfst du wie jede Datei mit datenbank.exists() aus Block 4. Bei MySQL gibt es dafür eigene Befehle wie SHOW DATABASES und SHOW TABLES.
ALTER TABLE nachträglich nur Spalten hinzufügen oder umbenennen, einen Primärschlüssel aber nicht mehr ergänzen.Übungsaufgaben
Die Aufgaben 3 bis 7 sind die Kernaufgaben, wer schneller ist, macht mit den Bonusaufgaben weiter. Schreibe alle Aufgaben in deine Datei block_05_sql_grundlagen.py aus Aufgabe 1. Sie beginnt immer mit diesem Kopf:
import sqlite3
from pathlib import Path
ORDNER = Path(__file__).resolve().parent
Aufgabe 3 – Die Schuldatenbank
Zeit: ca. 10 Minuten
Für die Schulverwaltung brauchst du eine Datenbank schule.db mit zwei Tabellen.
- Tabelle
klasse: Nummer (Primärschlüssel), Name wie „10a“ (Pflicht, eindeutig), Raum und Klassenlehrkraft (beides Text). - Tabelle
schueler: Nummer (Primärschlüssel, automatisch), Vorname und Nachname (Pflicht), Geburtsjahr (ganze Zahl) und die Nummer der Klasse (ganze Zahl). - Gib alle Tabellen der Datenbank aus, ohne die interne Tabelle
sqlite_sequence. Kontrolle: klasse, schueler. - Gib für beide Tabellen die Spalten in der Form
schueler.vorname (TEXT, Pflicht)aus.
Zusatz: Warum speichert schueler nur die Nummer der Klasse und nicht den Namen „10a“? Antworte als Kommentar. Stichwort: doppelte Daten.
Aufgabe 4 – Fehlersuche im Bauplan
Zeit: ca. 8 Minuten
Dieser Bauplan für eine Tabelle lehrkraft enthält drei Fehler. SQLite meldet immer nur den ersten.
bauplan = """
CREATE TABLE IF NOT EXISTS lehrkraft (
id INTEGER PRIMARY KEY AUTOINCREMENT
name TEXT NOT NULL,
fach TEXT,
order INTEGER,
)
"""
- Führe den Befehl in
schule.dbaus und lies die Fehlermeldung genau. - Behebe die Fehler einzeln und starte nach jeder Korrektur neu. Schreibe zu jedem Fehler einen Kommentar, was falsch war.
- Kontrolle: Die Tabelle wird ohne Fehler angelegt und hat 4 Spalten.
Tipp: order ist ein SQL-Schlüsselwort (es kommt in Block 6 bei ORDER BY vor) und darf nicht als Spaltenname verwendet werden.
Aufgabe 5 – Gibt es die Tabelle schon?
Zeit: ca. 12 Minuten
Programme prüfen oft zuerst, was in einer Datenbank steht. Schreibe dafür zwei Funktionen für deine schule.db aus Aufgabe 3.
- Schreibe
tabelle_existiert(cursor, name), die mit einer Abfrage aufsqlite_masterTrueoderFalsezurückgibt. Setze den Tabellennamen mit dem Platzhalter?ein, nicht mit einem f-String. - Teste:
schuelerergibtTrue,lehrerergibtFalse. - Schreibe
spalten(cursor, name), die die Spaltennamen einer Tabelle als Liste zurückgibt. Kontrolle fürklasse:['id', 'name', 'raum', 'lehrkraft']. - Frage mit
input()nach einem Tabellennamen und gib aus, ob es sie gibt und welche Spalten sie hat.
Zusatz: Lagere beide Funktionen in ein eigenes Modul datenbank.py aus und importiere sie (Block 2).
Aufgabe 6 – Planen für den Sportverein
Zeit: ca. 12 Minuten
Ein Sportverein will Mitglieder, Mannschaften und Trainingszeiten verwalten. Diesmal planst du die Tabellen selbst.
- Überlege, welche Tabellen und Spalten nötig sind. Schreibe den Plan zuerst als Kommentar auf (Tabelle: Spalten mit Typ).
- Jede Tabelle bekommt einen Primärschlüssel. Pflichtangaben bekommen
NOT NULL, Werte, die es nur einmal geben darf (z. B. die E-Mail-Adresse), bekommenUNIQUE. - Lege die Tabellen in
verein.dban und gib zur Kontrolle alle Tabellen mit ihren Spalten aus.
Zusatz: Ein Mitglied kann in mehreren Mannschaften spielen und eine Mannschaft hat viele Mitglieder. Wie bildest du das ab? Tipp: eine dritte Tabelle, die nur zwei Nummern speichert.
Aufgabe 7 – Neu anfangen
Zeit: ca. 8 Minuten
Beim Testen will man oft mit einer leeren Datenbank neu starten. Verbinde dafür Block 4 und Block 5.
- Frage: „Datenbank schule.db neu anlegen? (ja/nein)“.
- Bei „ja“ löschst du die Datei mit
unlink()(Block 4), aber nur, wenn es sie gibt. Danach legst du die beiden Tabellen aus Aufgabe 3 neu an. - Gib zum Schluss die Tabellen und die Dateigröße aus.
- Denkfrage als Kommentar: Was passiert unter Windows, wenn du die Datei löschen willst, während noch eine Verbindung zu ihr offen ist?
Bonusaufgabe 1 – Der Datenbank-Inspektor
Ein kleines Werkzeug soll zeigen, was in allen Datenbanken im Skript-Ordner steckt.
- Gehe mit
ORDNER.glob("*.db")alle Datenbankdateien durch und gib Name und Größe aus. - Gib für jede Datei ihre Tabellen aus (ohne interne
sqlite_-Tabellen). - Gib für jede Tabelle die Spalten als ausgerichtete Tabelle aus: Name, Typ und eine Markierung
PKfür den Primärschlüssel undPflichtfürNOT NULL.
Bonusaufgabe 2 – Nachträglich ändern
Manchmal fällt erst später auf, dass eine Spalte fehlt. SQLite kann eine bestehende Tabelle mit ALTER TABLE nur eingeschränkt ändern.
- Füge der Tabelle
schuelermitALTER TABLE schueler ADD COLUMN email TEXTeine Spalte hinzu und zeige die Spalten an. - Führe denselben Befehl noch einmal aus. Fange den Fehler ab und gib ihn aus.
- Versuche, mit
ADD COLUMNeine zweite Spalte mitPRIMARY KEYanzulegen. Was meldet SQLite? Erkläre als Kommentar, warum man den Primärschlüssel gleich beim Anlegen planen muss.
- SQLite speichert eine ganze Datenbank in einer Datei,
sqlite3ist in Python schon enthalten. - Ablauf:
connect()→cursor()→execute()→commit()→close(). CREATE TABLE IF NOT EXISTS name (spalte TYP REGELN, …)legt eine Tabelle an. Datentypen:INTEGER,REAL,TEXT.- Jede Tabelle braucht einen Primärschlüssel, meist
id INTEGER PRIMARY KEY AUTOINCREMENT. Weitere Regeln:NOT NULL,UNIQUE,DEFAULT. - Tabellen anzeigen:
sqlite_master, Spalten anzeigen:PRAGMA table_info(name), Ergebnis mitfetchall()abholen.
