Czym jest SQLite3?
- SQLite3 to lekka, wbudowana relacyjna baza danych SQL
- Nie wymaga osobnego serwera – cała baza to jeden plik
- Python ma wbudowany moduł sqlite3 – nie trzeba nic instalować
- Idealna do nauki, prototypów i małych/średnich aplikacji
SQLite3 to lekka, wbudowana relacyjna baza danych SQL
Nie wymaga osobnego serwera – cała baza to jeden plik
Python ma wbudowany moduł sqlite3 – nie trzeba nic instalować
Idealna do nauki, prototypów i małych/średnich aplikacji
Podstawowe pojęcia:
Połączenie (Connection) – łącze z plikiem bazy danych
Kursor (Cursor) – obiekt do wykonywania poleceń SQL
Commit – zatwierdzenie zmian w bazie
Rollback – cofnięcie niezatwierdzonych zmian
Analogia ze świata rzeczywistego:
Wyobraź sobie, że:
- Baza danych = Biblioteka pełna książek
- Connection (połączenie) = Karta biblioteczna (pozwala Ci wejść do biblioteki)
- Cursor (kursor) = Bibliotekarz, który wykonuje dla Ciebie konkretne zadania
- Dlaczego potrzebujemy kursora?
Nie możesz bezpośrednio rozmawiać z bazą danych! Potrzebujesz „pośrednika”, który:
- Zaniesie Twoje polecenie do bazy danych
- Wykona operację (znajdzie dane, doda coś, usunie)
- Przyniesie Ci wyniki
Tym pośrednikiem jest właśnie KURSOR.
import sqlite3
# KROK 1: Otwierasz drzwi do biblioteki (połączenie)
conn = sqlite3.connect('mojabaza.db')
# Teraz jesteś w bibliotece, ale jeszcze nic nie możesz zrobić
# KROK 2: Wzywasz bibliotekarza (tworzysz kursor)
cursor = conn.cursor()
# Teraz masz "pomocnika", któremu możesz wydawać polecenia
# KROK 3: Wydajesz polecenie bibliotekarzowi
cursor.execute('SELECT * FROM ksiazki WHERE autor LIKE ?', ('%Tolkien%',))
# Bibliotekarz idzie, szuka książek Tolkiena
# KROK 4: Bibliotekarz przynosi Ci wyniki
wyniki = cursor.fetchall()
print(wyniki)
# KROK 5: Wychodzisz z biblioteki
conn.close()
PRZYKŁADY PRAKTYCZNE
Przykład 1: Podstawowe operacje CRUD
tworzymy plik o nazwie crud_basics.py
import sqlite3
# Połączenie z bazą (tworzy plik jeśli nie istnieje)
conn = sqlite3.connect('mojabaza.db')
cursor = conn.cursor()
# CREATE - Tworzenie tabeli
cursor.execute('''
CREATE TABLE IF NOT EXISTS pracownicy (
id INTEGER PRIMARY KEY AUTOINCREMENT,
imie TEXT NOT NULL,
nazwisko TEXT NOT NULL,
wiek INTEGER,
wynagrodzenie REAL
)
''')
# INSERT - Wstawianie pojedynczego rekordu
cursor.execute('''
INSERT INTO pracownicy (imie, nazwisko, wiek, wynagrodzenie)
VALUES (?, ?, ?, ?)
''', ('Jan', 'Kowalski', 35, 5500.50))
# INSERT - Wstawianie wielu rekordów
dane = [
('Anna', 'Nowak', 28, 6200.00),
('Kacper', 'Malinowski', 22, 4100.75),
('Maria', 'Wiśniewska', 45, 7800.00)
]
cursor.executemany('''
INSERT INTO pracownicy (imie, nazwisko, wiek, wynagrodzenie)
VALUES (?, ?, ?, ?)
''', dane)
# Zatwierdzenie zmian
conn.commit()
# SELECT - Pobieranie wszystkich danych
cursor.execute('SELECT * FROM pracownicy')
wyniki = cursor.fetchall()
print("Wszyscy pracownicy:")
for wiersz in wyniki:
print(wiersz)
# SELECT - Pobieranie z warunkiem
cursor.execute('SELECT imie, nazwisko FROM pracownicy WHERE wiek > ?', (30,))
starsi = cursor.fetchall()
print("\nPracownicy powyżej 30 lat:")
for osoba in starsi:
print(f"{osoba[0]} {osoba[1]}")
# UPDATE - Aktualizacja danych
cursor.execute('''
UPDATE pracownicy
SET wynagrodzenie = wynagrodzenie * 1.1
WHERE wiek < 30
''')
conn.commit()
# DELETE - Usuwanie danych
cursor.execute('DELETE FROM pracownicy WHERE id = ?', (1,))
conn.commit()
# Zamknięcie połączenia
conn.close()
Przykład 2: Context Manager (zalecane podejście)
tworzymy plik o nazwie context_manager.py
import sqlite3
# Automatyczne zarządzanie połączeniem
with sqlite3.connect('sklep.db') as conn:
cursor = conn.cursor()
# Tworzenie tabeli produktów
cursor.execute('''
CREATE TABLE IF NOT EXISTS produkty (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nazwa TEXT NOT NULL,
cena REAL NOT NULL,
ilosc INTEGER DEFAULT 0
)
''')
# Dane są automatycznie commitowane przy wyjściu z bloku 'with'
cursor.execute('INSERT INTO produkty (nazwa, cena, ilosc) VALUES (?, ?, ?)',
('Laptop', 3500.00, 5))
# Pobieranie z użyciem row_factory dla dostępu jak do słownika
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('SELECT * FROM produkty')
for row in cursor:
print(f"Produkt: {row['nazwa']}, Cena: {row['cena']} zł")
Przykład 3: Zaawansowane zapytania
tworzymy plik o nazwie advanced_queries.py
import sqlite3
with sqlite3.connect('firma.db') as conn:
cursor = conn.cursor()
# Tworzenie dwóch powiązanych tabel
cursor.execute('''
CREATE TABLE IF NOT EXISTS dzialy (
id INTEGER PRIMARY KEY,
nazwa TEXT NOT NULL
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS pracownicy (
id INTEGER PRIMARY KEY,
imie TEXT NOT NULL,
nazwisko TEXT NOT NULL,
dzial_id INTEGER,
FOREIGN KEY (dzial_id) REFERENCES dzialy(id)
)
''')
# Wstawienie działów
dzialy = [(1, 'IT'), (2, 'HR'), (3, 'Sprzedaż')]
cursor.executemany('INSERT OR IGNORE INTO dzialy VALUES (?, ?)', dzialy)
# Wstawienie pracowników
pracownicy = [
('Jan', 'Kowalski', 1),
('Anna', 'Nowak', 2),
('Piotr', 'Wiśniewski', 1)
]
cursor.executemany('INSERT INTO pracownicy (imie, nazwisko, dzial_id) VALUES (?, ?, ?)', pracownicy)
# JOIN - łączenie tabel
cursor.execute('''
SELECT p.imie, p.nazwisko, d.nazwa
FROM pracownicy p
JOIN dzialy d ON p.dzial_id = d.id
WHERE d.nazwa = 'IT'
''')
print("Pracownicy działu IT:")
for row in cursor.fetchall():
print(f"{row[0]} {row[1]} - {row[2]}")
# GROUP BY - agregacja
cursor.execute('''
SELECT d.nazwa, COUNT(p.id) as liczba_pracownikow
FROM dzialy d
LEFT JOIN pracownicy p ON d.id = p.dzial_id
GROUP BY d.nazwa
''')
print("\nLiczba pracowników w działach:")
for row in cursor.fetchall():
print(f"{row[0]}: {row[1]}")
# Przykład 4: Obsługa błędów
tworzymy plik o nazwie error_handling.py
import sqlite3
try:
with sqlite3.connect('test.db') as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE NOT NULL
)
''')
# Próba wstawienia duplikatu
cursor.execute('INSERT INTO users (email) VALUES (?)', ('jan@example.com',))
cursor.execute('INSERT INTO users (email) VALUES (?)', ('jan@example.com',))
except sqlite3.IntegrityError as e:
print(f"Błąd integralności danych: {e}")
except sqlite3.OperationalError as e:
print(f"Błąd operacyjny: {e}")
except sqlite3.Error as e:
print(f"Błąd bazy danych: {e}")
Typy wyjątków:
| sqlite3.Warning | Ostrzeżenie |
| sqlite3.Error | Bazowy wyjątek dla wszystkich błędów |
| sqlite3.InterfaceError | Błąd interfejsu bazy |
| sqlite3.DatabaseError | Błąd związany z bazą |
| sqlite3.DataError | Błąd danych (np. dzielenie przez zero) |
| sqlite3.OperationalError | Błąd operacyjny (np. brak tabeli) |
| sqlite3.IntegrityError | Naruszenie integralności (np. UNIQUE) |
| sqlite3.InternalError | Błąd wewnętrzny SQLite |
| sqlite3.ProgrammingError | Błąd programowania (np. zły SQL) |
| sqlite3.NotSupportedError | Nieobsługiwana operacja |
Przykład 5:
#https://pypi.org/project/sqlite-database/
#shift + ctrl + p -> wpisujemy sqlite i wybieramy naszą bazę (test.db) - po lewej na dole pokazuje się dostęp do bazy
#tworzymy połączenie z bazą
import sqlite3
conn = sqlite3.connect('test1.db') #tworzymy połączenie z bazą danych przechowywaną w pliku na dysku (test.db, nazwa pliku jest dowolona) lub w pamięci, jeśli podamy ':memory:'
conn.row_factory = sqlite3.Row #dostęp do kolumn (pól tabel) nie tylko przez indeksy, ale również przez nazwy
db = conn.cursor() #obiekt kursora do wykonywania operacji na bazie danych
print(db)
#tworzymy tabele
#pojedyncze polecenia SQL-a wykonujemy za pomocą metody .execute()
db.execute("DROP TABLE IF EXISTS klasa;")
db.execute("""
CREATE TABLE IF NOT EXISTS klasa (
id INTEGER PRIMARY KEY ASC,
nazwa varchar(250) NOT NULL,
profil varchar(250) DEFAULT ''
)""")
#wiele instrukcji wykonujemy za pomocą metody .executescript()
db.executescript("""
DROP TABLE IF EXISTS uczen;
CREATE TABLE IF NOT EXISTS uczen (
id INTEGER PRIMARY KEY ASC,
imie varchar(250) NOT NULL,
nazwisko varchar(250) NOT NULL,
klasa_id INTEGER NOT NULL,
FOREIGN KEY(klasa_id) REFERENCES klasa(id)
)""")
#po uruchomieniu kodu baza pojawi się w katalogu
# wstawiamy jeden rekord danych
db.execute('INSERT INTO klasa VALUES(NULL, ?, ?);', ('1A', 'matematyczny'))
db.execute('INSERT INTO klasa VALUES(NULL, ?, ?);', ('1B', 'humanistyczny'))
#Polecenia CREATE TABLE (a także DROP TABLE, ALTER TABLE) są traktowane przez SQLite jako DDL (Data Definition Language) i są automatycznie zatwierdzane — SQLite implicitnie wykonuje COMMIT po każdej takiej operacji.Ale już INSERT, UPDATE, DELETE tego nie robią automatycznie: Te operacje to DML (Data Manipulation Language) i wymagają jawnego conn.commit(), jeśli chcesz, żeby zmiany zostały zapisane na stałe.
conn.commit()
# Wyświetlamy zawartość tabeli klasa
print("Zawartość tabeli 'klasa':")
for row in db.execute('SELECT * FROM klasa'):
print(dict(row)) # wypisujemy jako słownik, żeby było czytelniej
# wykonujemy zapytanie SQL, które pobierze id klasy "1A" z tabeli "klasa".
db.execute('SELECT id FROM klasa WHERE nazwa = ?', ('1A',))
klasa_id = db.fetchone()[0]
# tupla "uczniowie" zawiera tuple z danymi poszczególnych uczniów
uczniowie = (
(None, 'Tomasz', 'Nowak', klasa_id),
(None, 'Jan', 'Kos', klasa_id),
(None, 'Piotr', 'Kowalski', klasa_id)
)
# wstawiamy wiele rekordów
db.executemany('INSERT INTO uczen VALUES(?,?,?,?)', uczniowie)
# pobieranie danych z bazy
def czytajdane():
"""Funkcja pobiera i wyświetla dane z bazy."""
#poniżej potrójny cudzysłów został wykorzystany do zapisania długielo zapytania SQL
db.execute(
"""
SELECT uczen.id,imie,nazwisko,nazwa FROM uczen,klasa
WHERE uczen.klasa_id=klasa.id
""")
uczniowie = db.fetchall()
for uczen in uczniowie:
print(uczen['id'], uczen['imie'], uczen['nazwisko'], uczen['nazwa'])
print()
czytajdane()
# zmiana klasy ucznia o identyfikatorze 2res
db.execute('SELECT id FROM klasa WHERE nazwa = ?', ('1B',))
klasa_id = db.fetchone()[0]
db.execute('UPDATE uczen SET klasa_id=? WHERE id=?', (klasa_id, 2))
# usunięcie ucznia o identyfikatorze 3
# db.execute('DELETE FROM uczen WHERE id=?', (3,))
czytajdane()
# zatwierdzamy zmiany w bazie
conn.commit()
# Zamykamy połączenie
conn.close()
ĆWICZENIA
Ćwiczenie 1: Biblioteka książek (Podstawowe)
Utwórz bazę danych biblioteki z tabelą ksiażki zawierającą:
id (PRIMARY KEY)
tytul (TEXT)
autor (TEXT)
rok_wydania (INTEGER)
dostepna (INTEGER – 0 lub 1)
Zadania:
Dodaj 5 książek do tabeli
Wyświetl wszystkie książki wydane po 2000 roku
Oznacz książkę jako wypożyczoną (dostepna = 0)
Policz ile jest dostępnych książek
Ćwiczenie 2: Sklep internetowy (Średniozaawansowane)
Stwórz system zarządzania produktami z bazą o nazwie sklep.db. Utwórz dwie tabele: kategorie i produkty.
Tabela kategorie: id, nazwa
Tabela produkty: id, nazwa, cena, kategoria_id, stan_magazynowy
Utwórz obie tabele z odpowiednimi kluczami obcymi
Zadania:
Dodaj 3 kategorie (Elektronika, Odzież, Książki)
Dodaj po 3 produkty do każdej kategorii
Napisz zapytanie wyświetlające produkty wraz z nazwą kategorii
Oblicz średnią cenę produktów w każdej kategorii
Znajdź najdroższy produkt w bazie
Ćwiczenie 3: System zarządzania studentami z relacjami wiele-do-wielu: (Zaawansowane)
Utwórz bazę o nazwie uczelnia.db z trzema tabelami: studenci, kursy, zapisy (tabela łącząca)
Utwórz wszystkie trzy tabele z odpowiednimi kluczami obcymi i ograniczeniem UNIQUE(student_id, kurs_id)
Zaimplementuj funkcje:
dodaj_studenta(imie, nazwisko, rok_studiow)
dodaj_kurs(nazwa, punkty_ects)
zapisz_na_kurs(student_id, kurs_id, ocena)
wyswietl_kursy_studenta(student_id)
oblicz_srednia_studenta(student_id)
Użyj transakcji do zapisania studenta na wiele kursów jednocześnie
NAJWAŻNIEJSZE ZASADY
1. Bezpieczeństwo – SQL Injection
#ŹLE - podatne na SQL Injection
nazwa = input("Podaj nazwę: ")
cursor.execute(f"SELECT * FROM users WHERE name = '{nazwa}'")
#DOBRZE - używaj parametrów
nazwa = input("Podaj nazwę: ")
cursor.execute("SELECT * FROM users WHERE name = ?", (nazwa,))
2. Zawsze używaj commit()
cursor.execute('INSERT INTO ...')
conn.commit() # BEZ TEGO dane nie zostaną zapisane!
3. Zamykaj połączenia
#Sposób 1: Manualnie
conn = sqlite3.connect('db.db')
... operacje ...
conn.close()
#Sposób 2: Context manager (zalecane)
with sqlite3.connect('db.db') as conn:
... operacje ...
Automatyczne zamknięcie
PRZYDATNE METODY
| execute(sql, params) | Wykonuje pojedyncze polecenie SQL z opcjonalnymi parametrami |
| executemany(sql, seq_of_parameters) | Wstawia wiele rekordów |
| executescript(sql) | Wykonuje wiele poleceń SQL |
| fetchone() | Pobiera jeden wiersz |
| fetchall() | Pobiera wszystkie wiersze |
| fetchmany(n) | Pobiera n wierszy |
| lastrowid | ID ostatnio wstawionego rekordu |
| rowcount | Liczba zmienionych wierszy |
Dlaczego dwa kroki? (Connection + Cursor)
Dlaczego nie może być tak:
conn = sqlite3.connect('baza.db')
conn.execute('SELECT...') # Czy to nie wystarczy?
Może być! Ale to jest skrót:
#Sposób 1: SKRÓT (Python robi to za Ciebie)
conn.execute('SELECT * FROM users')
# Sposób 2: PEŁNY (tak naprawdę działa)
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
# Python w skrócie automatycznie tworzy kursor, ale go nie widzisz!
Możesz mieć WIELU BIBLIOTEKARZY naraz (wiele kursorów) i każdy może wykonywać różne zadania, ale wszyscy korzystają z tej samej karty bibliotecznej (połączenia). To pozwala na równoczesną pracę z bazą danych.
conn = sqlite3.connect('baza.db')
# Pierwszy bibliotekarz zajmuje się użytkownikami
cursor1 = conn.cursor()
cursor1.execute('SELECT * FROM users')
# Drugi bibliotekarz zajmuje się zamówieniami
cursor2 = conn.cursor()
cursor2.execute('SELECT * FROM orders')
# Każdy robi swoją robotę niezależnie!
users = cursor1.fetchall()
orders = cursor2.fetchall()
# Kursor "pamięta" wyniki swoich zapytań, więc możesz mieć wielu kursorów, a każdy z nich będzie miał swoje własne wyniki.
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
# Pobierasz wyniki STOPNIOWO
pierwszy = cursor.fetchone() # Pobiera 1 wiersz
drugi = cursor.fetchone() # Pobiera kolejny wiersz
reszta = cursor.fetchall() # Pobiera wszystkie pozostałe
# Gdybyś użył conn.execute(), nie miałbyś takiej kontroli nad kursorami i wynikami.
Pamiętaj
- pojedyncze polecenia SQL-a wykonujemy za pomocą metody .execute() np.
DROP TABLE IF EXISTS uczen; - wiele instrukcji wykonujemy za pomocą metody .executescript() np.
DROP TABLE IF EXISTS uczen; CREATE TABLE IF NOT EXISTS uczen (...); - dodanie wielu rekordów do tabeli wykonujemy za pomocą metody .executemany()
- Polecenia CREATE TABLE (a także DROP TABLE, ALTER TABLE) oraz SELECT są traktowane przez SQLite jako DDL (Data Definition Language) i są automatycznie zatwierdzane — SQLite implicitnie wykonuje COMMIT po każdej takiej operacji.Ale już INSERT, UPDATE, DELETE tego nie robią automatycznie: Te operacje to DML (Data Manipulation Language) i wymagają jawnego conn.commit(), jeśli chcesz, żeby zmiany zostały zapisane na stałe.
ROZWIĄZANIA
Rozwiązanie ćwiczenia 1: Biblioteka książek
import sqlite3
with sqlite3.connect('biblioteka.db') as conn:
c = conn.cursor()
# 1. Tworzenie tabeli
c.execute('''
CREATE TABLE IF NOT EXISTS ksiazki (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tytul TEXT NOT NULL,
autor TEXT NOT NULL,
rok_wydania INTEGER,
dostepna INTEGER DEFAULT 1
)
''')
# 2. Dodawanie książek
ksiazki = [
('Władca Pierścieni', 'J.R.R. Tolkien', 1954, 1),
('Harry Potter', 'J.K. Rowling', 1997, 1),
('Wiedźmin', 'Andrzej Sapkowski', 1990, 1),
('Metro 2033', 'Dmitry Glukhovsky', 2005, 1),
('Kod Da Vinci', 'Dan Brown', 2003, 1)
]
c.executemany('INSERT INTO ksiazki (tytul, autor, rok_wydania, dostepna) VALUES (?, ?, ?, ?)', ksiazki)
# 3. Książki po 2000 roku
print("Książki wydane po 2000 roku:")
c.execute('SELECT tytul, autor, rok_wydania FROM ksiazki WHERE rok_wydania > 2000')
for row in c.fetchall():
print(f"- {row[0]} ({row[1]}, {row[2]})")
# 4. Wypożyczenie książki
c.execute('UPDATE ksiazki SET dostepna = 0 WHERE tytul = ?', ('Metro 2033',))
# 5. Liczba dostępnych książek
c.execute('SELECT COUNT(*) FROM ksiazki WHERE dostepna = 1')
liczba = c.fetchone()[0]
print(f"\nDostępnych książek: {liczba}")
Rozwiązanie ćwiczenia 2: Sklep internetowy
import sqlite3
with sqlite3.connect('sklep.db') as conn:
c = conn.cursor()
# 1. Tworzenie tabel
c.execute('''
CREATE TABLE IF NOT EXISTS kategorie (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nazwa TEXT NOT NULL UNIQUE
)
''')
c.execute('''
CREATE TABLE IF NOT EXISTS produkty (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nazwa TEXT NOT NULL,
cena REAL NOT NULL,
kategoria_id INTEGER,
stan_magazynowy INTEGER DEFAULT 0,
FOREIGN KEY (kategoria_id) REFERENCES kategorie(id)
)
''')
# 2. Dodawanie kategorii
kategorie = [('Elektronika',), ('Odzież',), ('Książki',)]
c.executemany('INSERT OR IGNORE INTO kategorie (nazwa) VALUES (?)', kategorie)
# 3. Dodawanie produktów
produkty = [
('Laptop', 3500.00, 1, 10),
('Smartphone', 2200.00, 1, 15),
('Słuchawki', 450.00, 1, 25),
('Koszulka', 89.99, 2, 50),
('Spodnie', 199.99, 2, 30),
('Kurtka', 499.00, 2, 20),
('Python dla każdego', 59.99, 3, 100),
('Clean Code', 89.00, 3, 75),
('Algorytmy', 120.00, 3, 40)
]
c.executemany('INSERT INTO produkty (nazwa, cena, kategoria_id, stan_magazynowy) VALUES (?, ?, ?, ?)', produkty)
# 4. Produkty z kategoriami
print("Produkty z kategoriami:")
c.execute('''
SELECT p.nazwa, p.cena, k.nazwa
FROM produkty p
JOIN kategorie k ON p.kategoria_id = k.id
ORDER BY k.nazwa
''')
for row in c.fetchall():
print(f"{row[0]} ({row[2]}) - {row[1]} zł")
# 5. Średnia cena w kategoriach
print("\nŚrednia cena w kategoriach:")
c.execute('''
SELECT k.nazwa, ROUND(AVG(p.cena), 2) as srednia
FROM kategorie k
JOIN produkty p ON k.id = p.kategoria_id
GROUP BY k.nazwa
''')
for row in c.fetchall():
print(f"{row[0]}: {row[1]} zł")
# 6. Najdroższy produkt
c.execute('SELECT nazwa, cena FROM produkty ORDER BY cena DESC LIMIT 1')
najdrozszy = c.fetchone()
print(f"\nNajdroższy produkt: {najdrozszy[0]} ({najdrozszy[1]} zł)")
Rozwiązanie ćwiczenia 3: System zarządzania studentami
import sqlite3
class SystemStudentow:
def __init__(self, db_name='uczelnia.db'):
# Polaczenie z baza danych
self.conn = sqlite3.connect(db_name)
# Ustawienie row_factory dla dostepu do kolumn jak do slownika
self.conn.row_factory = sqlite3.Row
self._utworz_tabele()
def _utworz_tabele(self):
# Utworzenie kursora
cursor = self.conn.cursor()
# Tworzenie tabeli studenci
cursor.execute('''
CREATE TABLE IF NOT EXISTS studenci (
id INTEGER PRIMARY KEY AUTOINCREMENT,
imie TEXT NOT NULL,
nazwisko TEXT NOT NULL,
rok_studiow INTEGER
)
''')
# Tworzenie tabeli kursy
cursor.execute('''
CREATE TABLE IF NOT EXISTS kursy (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nazwa TEXT NOT NULL,
punkty_ects INTEGER
)
''')
# Tworzenie tabeli zapisy (relacja wiele-do-wielu)
cursor.execute('''
CREATE TABLE IF NOT EXISTS zapisy (
id INTEGER PRIMARY KEY AUTOINCREMENT,
student_id INTEGER,
kurs_id INTEGER,
ocena REAL,
FOREIGN KEY (student_id) REFERENCES studenci(id),
FOREIGN KEY (kurs_id) REFERENCES kursy(id),
UNIQUE(student_id, kurs_id)
)
''')
# Zatwierdzenie zmian
self.conn.commit()
def dodaj_studenta(self, imie, nazwisko, rok_studiow):
cursor = self.conn.cursor()
# Wstawienie studenta
cursor.execute(
'INSERT INTO studenci (imie, nazwisko, rok_studiow) VALUES (?, ?, ?)',
(imie, nazwisko, rok_studiow)
)
self.conn.commit()
# Zwrocenie ID dodanego rekordu
return cursor.lastrowid
def dodaj_kurs(self, nazwa, punkty_ects):
cursor = self.conn.cursor()
# Wstawienie kursu
cursor.execute(
'INSERT INTO kursy (nazwa, punkty_ects) VALUES (?, ?)',
(nazwa, punkty_ects)
)
self.conn.commit()
return cursor.lastrowid
def zapisz_na_kurs(self, student_id, kurs_id, ocena=None):
cursor = self.conn.cursor()
try:
# Wstawienie zapisu studenta na kurs
cursor.execute(
'INSERT INTO zapisy (student_id, kurs_id, ocena) VALUES (?, ?, ?)',
(student_id, kurs_id, ocena)
)
self.conn.commit()
return True
except sqlite3.IntegrityError:
print("Student jest juz zapisany na ten kurs")
return False
def ustaw_ocene(self, student_id, kurs_id, ocena):
cursor = self.conn.cursor()
# Aktualizacja oceny
cursor.execute('''
UPDATE zapisy
SET ocena = ?
WHERE student_id = ? AND kurs_id = ?
''', (ocena, student_id, kurs_id))
# Sprawdzenie czy rekord zostal zaktualizowany
if cursor.rowcount == 0:
print("Student nie jest zapisany na ten kurs")
return False
self.conn.commit()
return True
def wyswietl_kursy_studenta(self, student_id):
cursor = self.conn.cursor()
# Zapytanie JOIN laczace tabele zapisy i kursy
cursor.execute('''
SELECT k.nazwa, k.punkty_ects, z.ocena
FROM zapisy z
JOIN kursy k ON z.kurs_id = k.id
WHERE z.student_id = ?
''', (student_id,))
print(f"\nKursy studenta (ID: {student_id}):")
# Odczyt wszystkich wierszy
for row in cursor.fetchall():
ocena_str = f"{row['ocena']}" if row['ocena'] else "brak"
print(f"- {row['nazwa']} ({row['punkty_ects']} ECTS) - Ocena: {ocena_str}")
def oblicz_srednia_studenta(self, student_id):
cursor = self.conn.cursor()
# Zapytanie z funkcja agregujaca AVG
cursor.execute('''
SELECT AVG(ocena) as srednia
FROM zapisy
WHERE student_id = ? AND ocena IS NOT NULL
''', (student_id,))
# Odczyt jednego wiersza
wynik = cursor.fetchone()
return round(wynik['srednia'], 2) if wynik['srednia'] else 0
def zapisz_na_wiele_kursow(self, student_id, kursy_ids):
try:
cursor = self.conn.cursor()
# Transakcja - wstawienie wielu rekordow
for kurs_id in kursy_ids:
cursor.execute(
'INSERT INTO zapisy (student_id, kurs_id) VALUES (?, ?)',
(student_id, kurs_id)
)
self.conn.commit()
print(f"Student zapisany na {len(kursy_ids)} kursow")
except sqlite3.Error as e:
# Wycofanie zmian w przypadku bledu
self.conn.rollback()
print(f"Blad: {e}")
def __del__(self):
# Zamkniecie polaczenia
self.conn.close()
if __name__ == '__main__':
import os
# Usuniecie starej bazy danych
if os.path.exists('uczelnia.db'):
os.remove('uczelnia.db')
system = SystemStudentow()
# Dodawanie studentow
student1 = system.dodaj_studenta('Jan', 'Kowalski', 2)
student2 = system.dodaj_studenta('Anna', 'Nowak', 1)
student3 = system.dodaj_studenta('Piotr', 'Wisniewski', 3)
# Dodawanie kursow
kurs1 = system.dodaj_kurs('Bazy Danych', 6)
kurs2 = system.dodaj_kurs('Algorytmy', 5)
kurs3 = system.dodaj_kurs('Sieci Komputerowe', 4)
# Zapisanie studenta 1 na wszystkie kursy
system.zapisz_na_wiele_kursow(student1, [kurs1, kurs2, kurs3])
# Ustawienie ocen dla studenta 1
system.ustaw_ocene(student1, kurs1, 4.5)
system.ustaw_ocene(student1, kurs2, 5.0)
system.ustaw_ocene(student1, kurs3, 3.5)
# Zapisanie studenta 2 na 2 kursy z ocenami
system.zapisz_na_kurs(student2, kurs1, 4.0)
system.zapisz_na_kurs(student2, kurs2, 5.0)
# Zapisanie studenta 3 na 1 kurs bez oceny
system.zapisz_na_kurs(student3, kurs1)
# Wyswietlanie kursow studentow
system.wyswietl_kursy_studenta(student1)
srednia1 = system.oblicz_srednia_studenta(student1)
print(f"\nSrednia ocen: {srednia1}")
system.wyswietl_kursy_studenta(student2)
srednia2 = system.oblicz_srednia_studenta(student2)
print(f"\nSrednia ocen: {srednia2}")
system.wyswietl_kursy_studenta(student3)
srednia3 = system.oblicz_srednia_studenta(student3)
print(f"\nSrednia ocen: {srednia3}")