Programování

SQLite v Pythonu: modul sqlite3 prakticky a bezpečně

Jak pracovat s SQLite v Pythonu – připojení, parametrizované dotazy, transakce, Row factory, indexy, WAL režim a zálohy. Bez SQL injection a zamčených databází.

12. 10. 2026 · 4 min čtení

SQLite je databáze v jednom souboru, bez serveru a bez konfigurace. Modul sqlite3 je součástí standardní knihovny Pythonu, takže nic neinstalujete. Pro skripty, menší weby, boty a desktopové aplikace je to často nejlepší volba.

Připojení a vytvoření tabulky

import sqlite3

conn = sqlite3.connect("app.db")
conn.execute("""
    CREATE TABLE IF NOT EXISTS ukoly (
        id INTEGER PRIMARY KEY,
        nazev TEXT NOT NULL,
        hotovo INTEGER NOT NULL DEFAULT 0,
        vytvoreno TEXT NOT NULL DEFAULT (datetime('now'))
    )
""")
conn.commit()

Soubor app.db se vytvoří automaticky. Pro testy použijte ":memory:" – databázi jen v paměti.

INTEGER PRIMARY KEY je v SQLite alias pro interní rowid, takže se hodnota přiděluje automaticky.

Parametrizované dotazy – jediný správný způsob

nazev = input("Název úkolu: ")

# ŠPATNĚ – SQL injection:
conn.execute(f"INSERT INTO ukoly (nazev) VALUES ('{nazev}')")

# SPRÁVNĚ:
conn.execute("INSERT INTO ukoly (nazev) VALUES (?)", (nazev,))

Pozor na čárku v (nazev,) – bez ní to není n-tice. Lze použít i pojmenované parametry:

conn.execute(
    "UPDATE ukoly SET hotovo = :hotovo WHERE id = :id",
    {"hotovo": 1, "id": 5},
)

Parametry fungují jen pro hodnoty. Názvy tabulek a sloupců parametrizovat nelze – pokud je musíte skládat dynamicky, ověřte je proti pevnému seznamu povolených hodnot.

Čtení dat

conn.row_factory = sqlite3.Row

for radek in conn.execute("SELECT id, nazev, hotovo FROM ukoly WHERE hotovo = ?", (0,)):
    print(radek["id"], radek["nazev"])

jeden = conn.execute("SELECT * FROM ukoly WHERE id = ?", (1,)).fetchone()
if jeden is None:
    print("Nenalezeno")

sqlite3.Row umožňuje přístup podle názvu sloupce i indexu a převod na slovník přes dict(radek).

Transakce a context manager

Modul sqlite3 ve výchozím režimu automaticky otevírá transakci před INSERT/UPDATE/DELETE, ale commit musíte udělat sami. Nejpohodlnější je spojení použít jako context manager:

with conn:
    conn.execute("INSERT INTO ukoly (nazev) VALUES (?)", ("A",))
    conn.execute("INSERT INTO ukoly (nazev) VALUES (?)", ("B",))
# commit proběhne automaticky; při výjimce rollback

Pozor: with conn: spojení nezavírá. Pro uzavření použijte conn.close() nebo contextlib.closing.

Hromadné vkládání

data = [("Úkol 1",), ("Úkol 2",), ("Úkol 3",)]
with conn:
    conn.executemany("INSERT INTO ukoly (nazev) VALUES (?)", data)

Tisíce řádků v jedné transakci jsou řádově rychlejší než tisíce samostatných commitů.

Cizí klíče jsou ve výchozím stavu vypnuté

Tohle překvapí každého:

conn.execute("PRAGMA foreign_keys = ON")

Musí se nastavit pro každé spojení. Bez toho REFERENCES a ON DELETE CASCADE nic nedělají.

WAL režim a souběžný přístup

SQLite zvládá mnoho čtenářů, ale jen jednoho zapisovatele naráz. Pro web nebo bota s více vlákny zapněte WAL:

conn.execute("PRAGMA journal_mode = WAL")   # trvalé nastavení souboru
conn = sqlite3.connect("app.db", timeout=10)
  • WAL dovolí čtení během zápisu.
  • timeout (výchozí 5 s) určuje, jak dlouho čekat na zámek, než přijde database is locked.
  • Transakce držte krátké – nedělejte síťové požadavky uprostřed otevřené transakce.

Ve WAL režimu vedle databáze uvidíte soubory app.db-wal a app.db-shm. Jsou součástí databáze, nemažte je za běhu.

Indexy

conn.execute("CREATE INDEX IF NOT EXISTS idx_ukoly_hotovo ON ukoly(hotovo)")

Zda dotaz index využije, ověříte:

for r in conn.execute("EXPLAIN QUERY PLAN SELECT * FROM ukoly WHERE hotovo = 0"):
    print(r)

SCAN ukoly znamená průchod celou tabulkou, SEARCH ... USING INDEX je to, co chcete.

Datum a čas

SQLite nemá datový typ pro datum. Ukládejte ISO 8601 text (2026-10-12T14:30:00) – dá se řadit i porovnávat jako řetězec. Výchozí převodníky datetime v modulu sqlite3 jsou od Pythonu 3.12 označené jako zastaralé, takže převod dělejte explicitně přes datetime.isoformat() a datetime.fromisoformat().

Zálohování za běhu

Kopírovat soubor cp během zápisu není bezpečné. Použijte backup API:

src = sqlite3.connect("app.db")
dst = sqlite3.connect("zaloha.db")
with dst:
    src.backup(dst)
dst.close(); src.close()

Nebo z příkazové řádky: sqlite3 app.db ".backup zaloha.db".

Kdy SQLite nestačí

  • Mnoho souběžných zapisovatelů (desítky požadavků za sekundu se zápisem).
  • Více aplikačních serverů sdílejících jednu databázi přes síť – SQLite na síťovém disku (NFS) není spolehlivé.
  • Potřeba uživatelů a oprávnění na úrovni databáze.

V takovém případě přejděte na PostgreSQL. Pokud píšete SQL ručně a parametrizovaně, migrace bude jednodušší, než se zdá.