DELETE rimuove righe, nient'altro
DELETE toglie righe da una tabella. Non elimina la tabella, non ne cambia lo schema, non tocca altre tabelle (a meno che tu non abbia impostato delle cascate). La forma è breve:
DELETE FROM users WHERE id = 2; trova le righe che soddisfano la condizione e le rimuove. Le altre due righe restano intatte. La tabella esiste ancora: puoi continuare a inserirci dati.
Il modello mentale: DELETE è una SELECT che butta via le righe corrispondenti invece di restituirle.
È la clausola WHERE a fare tutto il lavoro
Ogni DELETE serio vive o muore con la sua clausola WHERE. Se è giusta, rimuovi ciò che volevi. Se è sbagliata, elimini di più, a volte tutta la tabella.
Entrambe le bozze non pubblicate e senza visualizzazioni sono sparite. Le righe pubblicate sopravvivono perché la condizione non le riguardava. Puoi usare qualsiasi espressione accettata da WHERE: IN, LIKE, BETWEEN, subquery, combinazioni di AND/OR.
Un'abitudine da prendere: prima di eseguire un DELETE, esegui la stessa clausola WHERE in una SELECT.
-- Anteprima di ciò che verrà eliminato:
SELECT * FROM posts WHERE published = 0 AND views = 0;
-- Le righe ti convincono? Ora eliminale:
DELETE FROM posts WHERE published = 0 AND views = 0;
Questo balletto in due passi ha salvato più database di tutti gli strumenti di backup messi insieme.
DELETE senza WHERE svuota la tabella
Ometti WHERE e DELETE rimuove ogni riga:
La tabella è vuota ma esiste ancora. SQLite non ha un'istruzione TRUNCATE: DELETE FROM table; è l'equivalente, e SQLite applica una "truncate optimization" interna che elimina tutte le pagine in una volta invece di rimuovere le righe una per una. È veloce, ma resta un'operazione transazionale che puoi annullare.
Se hai usato AUTOINCREMENT sulla chiave primaria, il contatore non si azzera da solo. Per far ripartire gli id da 1, cancella anche la riga della sequenza:
DELETE FROM log;
DELETE FROM sqlite_sequence WHERE name = 'log';
Con un semplice INTEGER PRIMARY KEY (senza AUTOINCREMENT), SQLite riutilizza già liberamente gli id, quindi non serve.
Eliminare più righe specifiche
IN è il modo più pulito per eliminare un insieme noto di righe:
Puoi anche guidare un'eliminazione con una subquery: comodo quando le righe da rimuovere sono definite da un join o da un'altra tabella:
SQLite non supporta la sintassi DELETE ... JOIN come fa MySQL, ma una subquery nel WHERE fa lo stesso lavoro.
RETURNING: vedere cosa hai eliminato
Aggiungi RETURNING per riavere le righe eliminate come risultato, proprio come una SELECT:
Ottieni l'id e l'email di ogni riga eliminata. È preziosissimo per:
- Registrare nei log esattamente cosa è stato rimosso.
- Creare funzioni di annullamento (salva da qualche parte le righe restituite).
- Confermare che un'eliminazione ha colpito le righe che ti aspettavi, in un solo viaggio.
RETURNING funziona con INSERT, UPDATE e DELETE. Viene trattato nel dettaglio nella sua pagina.
ON DELETE CASCADE per le righe collegate
Quando una tabella padre e una figlia sono collegate da una chiave esterna, eliminare il padre lascia figli orfani, a meno che tu non dica a SQLite di propagare l'eliminazione:
Eliminare l'autrice elimina anche i suoi libri. Senza ON DELETE CASCADE, lo stesso DELETE riuscirebbe lasciando libri orfani (se le chiavi esterne sono disattivate) oppure fallirebbe con un errore di vincolo (se sono attive).
La grande trappola: in SQLite le chiavi esterne sono disattivate di default. Devi eseguire PRAGMA foreign_keys = ON; per ogni connessione. Se il pragma non è impostato, ON DELETE CASCADE viene ignorato in silenzio e i libri restano. La maggior parte dei driver applicativi lo imposta per te o espone un'opzione; controlla il tuo.
Altre opzioni di cascata da conoscere: ON DELETE SET NULL (azzera la chiave esterna), ON DELETE RESTRICT (rifiuta l'eliminazione se esistono figli), ON DELETE NO ACTION (il default, nella maggior parte dei casi uguale a RESTRICT).
DELETE con LIMIT (opzione di compilazione)
Alcune build di SQLite supportano DELETE ... LIMIT, utile per smaltire tabelle enormi a blocchi:
DELETE FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000;
Serve che SQLite sia compilato con SQLITE_ENABLE_UPDATE_DELETE_LIMIT. I binari ufficiali e la maggior parte dei binding (il modulo sqlite3 di Python, better-sqlite3 per Node) lo hanno attivo. Se il tuo no, otterrai un errore di sintassi: ripiega su una subquery:
DELETE FROM logs
WHERE id IN (
SELECT id FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000
);
Le eliminazioni a blocchi mantengono piccole le transazioni, e questo conta quando altre connessioni stanno leggendo il database.
Racchiudi le grandi eliminazioni in una transazione
Un DELETE è implicitamente transazionale: o vanno via tutte le righe corrispondenti, o nessuna. Ma quando stai per eliminare molto, racchiuderlo in una transazione esplicita ti permette di fare ROLLBACK se qualcosa non torna:
ROLLBACK annulla del tutto l'eliminazione. In una sessione reale faresti COMMIT una volta verificato che il conteggio è giusto. Le transazioni sono anche molto più veloci quando elimini tante righe con un'istruzione alla volta: racchiudere il ciclo in BEGIN/COMMIT evita un fsync per ogni eliminazione.
Cose che non eliminano
Alcune confusioni comuni da segnalare:
DELETE FROM table;svuota la tabella ma non la elimina. UsaDROP TABLE table;per rimuovere la tabella stessa.DELETEnon riduce il file del database. Le pagine vengono segnate come libere per essere riutilizzate. Per recuperare spazio su disco, eseguiVACUUM;(trattato nel capitolo sulle prestazioni).- Eliminare una riga non elimina le righe figlie in altre tabelle, a meno che
ON DELETE CASCADEsia impostato e le chiavi esterne siano attive. - Un
DELETEche non trova nessuna riga non è un errore. È un'istruzione riuscita conchanges() = 0. Controlla il numero di righe se ti serve saperlo.
Prossimo passo: UPSERT
Spesso non vuoi davvero eliminare: vuoi inserire una riga se è nuova, o aggiornarla se esiste già. SQLite la chiama UPSERT, e la clausola ON CONFLICT la trasforma in una sola istruzione invece di tre. È il prossimo argomento.
Domande frequenti
Come si elimina una riga in SQLite?
Usa DELETE FROM table_name WHERE condition;. La clausola WHERE sceglie quali righe eliminare. Per esempio, DELETE FROM users WHERE id = 7; rimuove il singolo utente con id 7. Senza WHERE, viene eliminata ogni riga della tabella.
Come elimino tutte le righe di una tabella SQLite?
Esegui DELETE FROM table_name; senza clausola WHERE. SQLite non ha un'istruzione TRUNCATE: un DELETE senza filtro è l'equivalente, e SQLite lo ottimizza internamente (la 'truncate optimization'). Per azzerare anche i contatori AUTOINCREMENT, elimina poi le righe corrispondenti da sqlite_sequence.
SQLite può propagare le eliminazioni alle tabelle collegate?
Sì, se dichiari ON DELETE CASCADE sulla chiave esterna e hai attivato le chiavi esterne con PRAGMA foreign_keys = ON;. In SQLite le chiavi esterne sono disattivate di default, quindi il pragma conta: senza, le cascate vengono ignorate in silenzio.
Come vedo quali righe sono state eliminate?
Aggiungi una clausola RETURNING: DELETE FROM users WHERE active = 0 RETURNING id, email; restituisce le righe eliminate proprio come una SELECT. È utile per i log, per funzioni di annullamento o per confermare di aver cancellato esattamente ciò che volevi.