SQLite: il database che vive nel tuo file
Categoria: Database | Livello: Principiante–Intermedio
Cos'è SQLite e perché è diverso dagli altri DBMS
Quando si parla di database, la mente corre subito a sistemi complessi come MySQL o PostgreSQL: server da installare, utenti da configurare, porte da aprire. SQLite è una cosa completamente diversa.
SQLite è una libreria software che implementa un motore di database relazionale senza server (serverless). L'intero database — tabelle, indici, dati — è contenuto in un singolo file sul disco. Nessun processo in background, nessuna connessione di rete, nessuna configurazione: basta includere la libreria nel progetto e iniziare a lavorare.
> 💡 Curiosità: SQLite è probabilmente il database più diffuso al mondo. È integrato in Android, iOS, Firefox, Chrome, Python, e in migliaia di applicazioni desktop e mobili.
Architettura: serverless vs client-server
Per capire SQLite, è utile confrontarlo con un DBMS tradizionale.
| Caratteristica | MySQL / PostgreSQL | SQLite |
|---|---|---|
| Architettura | Client-server | Serverless (libreria embeddable) |
| Installazione | Server separato | Solo un file .db |
| Connessione | Rete TCP/IP | Accesso diretto al file |
| Utenti e permessi | Sì (complessi) | Solo i permessi del filesystem |
| Adatto a uso concorrente | Sì (molti utenti) | Limitato (un writer alla volta) |
| Portabilità | Dipende dal sistema | Il file è portabile ovunque |
In un sistema client-server, l'applicazione invia query attraverso la rete a un processo separato (il server) che gestisce il database. Con SQLite, invece, la libreria è incorporata direttamente nel programma: le query vengono eseguite in-process, senza intermediari.
Quando usare (e quando non usare) SQLite
✅ SQLite è la scelta giusta quando:
- Stai sviluppando un'applicazione desktop o mobile con dati locali
- Vuoi un prototipo veloce senza configurare un server
- Il tuo progetto ha accessi concorrenti limitati (pochi utenti, un utente alla volta)
- Hai bisogno di un formato di file strutturato invece di un CSV o JSON
- Stai creando test automatizzati e vuoi un database in memoria (
:memory:) - Stai imparando SQL e vuoi un ambiente leggero e portabile
❌ SQLite non è adatto quando:
- Più utenti scrivono contemporaneamente sul database (es. web app ad alto traffico)
- Hai bisogno di replicazione o clustering
- Gestisci dataset di dimensioni multi-terabyte
- Hai bisogno di controllo granulare degli accessi per utenti diversi
Installazione e primi passi
SQLite è disponibile su tutti i principali sistemi operativi.
Su Linux / Ubuntu
sudo apt update
sudo apt install sqlite3
Su Windows
Scarica il pacchetto precompilato da sqlite.org/download e aggiungi l'eseguibile al PATH.
Verifica dell'installazione
sqlite3 --version
# Output: 3.45.0 2024-01-15 ...
Creare un database e le prime tabelle
SQLite crea automaticamente il file database se non esiste.
sqlite3 scuola.db
Ora siamo nella shell interattiva di SQLite. Creiamo una tabella studenti:
CREATE TABLE studenti (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nome TEXT NOT NULL,
cognome TEXT NOT NULL,
classe TEXT,
voto_medio REAL
);
Inserimento di dati
INSERT INTO studenti (nome, cognome, classe, voto_medio)
VALUES ('Marco', 'Rossi', '4A', 7.5);
INSERT INTO studenti (nome, cognome, classe, voto_medio)
VALUES ('Giulia', 'Ferrari', '4A', 8.2);
INSERT INTO studenti (nome, cognome, classe, voto_medio)
VALUES ('Luca', 'Bianchi', '4B', 6.9);
Interrogazione dei dati
-- Tutti gli studenti
SELECT * FROM studenti;
-- Solo quelli della 4A con voto superiore a 7
SELECT nome, cognome, voto_medio
FROM studenti
WHERE classe = '4A' AND voto_medio > 7
ORDER BY voto_medio DESC;
Output:
Giulia|Ferrari|8.2
Marco|Rossi|7.5
Tipi di dato in SQLite
SQLite adotta un sistema di tipi flessibile chiamato type affinity (affinità di tipo). I tipi principali sono cinque:
| Tipo SQLite | Descrizione | Esempio |
|---|---|---|
INTEGER |
Intero con segno (1, 2, 4, 8 byte) | id = 42 |
REAL |
Floating point a 64 bit (IEEE 754) | voto = 7.5 |
TEXT |
Stringa di testo (UTF-8 o UTF-16) | nome = 'Mario' |
BLOB |
Dati binari grezzi | Immagini, file |
NULL |
Valore assente | Campo non compilato |
> ⚠️ Attenzione: SQLite è loosely typed. È possibile inserire una stringa in una colonna INTEGER senza errori (a meno che non venga usato STRICT). Questo comportamento, pur pratico, può essere fonte di bug in applicazioni grandi.
Comandi utili nella shell SQLite
I comandi che iniziano con . non sono SQL, ma meta-comandi della shell:
.tables -- Elenca tutte le tabelle
.schema studenti -- Mostra la struttura della tabella
.headers on -- Mostra i nomi delle colonne nei risultati
.mode column -- Formato colonne allineate
.output report.txt -- Reindirizza output su file
.read script.sql -- Esegue un file SQL esterno
.quit -- Esce dalla shell
SQLite in Python: un esempio pratico
Python include SQLite nella libreria standard con il modulo sqlite3. Non serve installare nulla.
import sqlite3
# Connessione al database (viene creato se non esiste)
conn = sqlite3.connect("scuola.db")
cursor = conn.cursor()
# Creazione tabella
cursor.execute("""
CREATE TABLE IF NOT EXISTS studenti (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nome TEXT NOT NULL,
cognome TEXT NOT NULL,
classe TEXT,
voto_medio REAL
)
""")
# Inserimento con parametri (prevenzione SQL Injection!)
dati = [
("Marco", "Rossi", "4A", 7.5),
("Giulia", "Ferrari", "4A", 8.2),
("Luca", "Bianchi", "4B", 6.9),
]
cursor.executemany(
"INSERT INTO studenti (nome, cognome, classe, voto_medio) VALUES (?, ?, ?, ?)",
dati
)
conn.commit()
# Lettura dei dati
cursor.execute("SELECT * FROM studenti WHERE voto_medio >= 7.5")
for riga in cursor.fetchall():
print(f"{riga[1]} {riga[2]} - Classe {riga[3]} - Media: {riga[4]}")
conn.close()
Output:
Marco Rossi - Classe 4A - Media: 7.5
Giulia Ferrari - Classe 4A - Media: 8.2
> 💡 Best practice: Usa sempre i parametri (?) invece di concatenare stringhe nelle query. Questo previene gli attacchi SQL Injection.
SQL Injection: perché i parametri sono fondamentali
Considera questo codice pericoloso:
# ❌ MAI fare così
nome_input = "'; DROP TABLE studenti; --"
cursor.execute(f"SELECT * FROM studenti WHERE nome = '{nome_input}'")
La query risultante sarebbe:
SELECT * FROM studenti WHERE nome = ''; DROP TABLE studenti; --'
Un attaccante potrebbe cancellare l'intero database! Usando i parametri, SQLite tratta l'input come un semplice valore e non come codice SQL:
# ✅ Corretto: input trattato come dato, non come codice
cursor.execute("SELECT * FROM studenti WHERE nome = ?", (nome_input,))
Esercizi proposti
Esercizio 1 — Base Crea un database biblioteca.db con una tabella libri contenente: id, titolo, autore, anno, disponibile (0 o 1). Inserisci almeno 5 libri e scrivi una query che elenci solo quelli disponibili.
Esercizio 2 — Intermedio Aggiungi una seconda tabella prestiti con i campi: id, id_libro (chiave esterna), data_prestito, data_restituzione. Scrivi una query che mostri i libri attualmente in prestito (restituzione NULL).
Esercizio 3 — Avanzato Scrivi un programma Python che permetta, tramite menu testuale, di: aggiungere un libro, registrare un prestito, restituire un libro e visualizzare i libri disponibili. Usa sempre parametri per le query.
Riepilogo
| Concetto | Punti chiave |
|---|---|
| Architettura | Serverless, tutto in un file .db |
| Tipi di dato | INTEGER, REAL, TEXT, BLOB, NULL |
| Quando usarlo | App locali, prototipi, test, dati strutturati monouser |
| Quando non usarlo | Alta concorrenza, grandi dataset, multi-utente con ACL |
| Sicurezza | Usa sempre i parametri ? per prevenire SQL Injection |
| Integrazione Python | Modulo sqlite3 incluso nella libreria standard |
Risorse per approfondire
- 📖 Documentazione ufficiale SQLite
- 🐍 Modulo sqlite3 in Python
- 🛠️ DB Browser for SQLite — interfaccia grafica gratuita
Articolo pubblicato su filippobilardo.it — Tutti i diritti riservati