Menu

DELETE in SQLite: eliminare righe in sicurezza con WHERE e RETURNING

Come funziona DELETE in SQLite: scrivere una clausola WHERE sicura, eliminare tutte le righe, propagare l'eliminazione alle tabelle collegate e riottenere le righe eliminate con RETURNING.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

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. Usa DROP TABLE table; per rimuovere la tabella stessa.
  • DELETE non riduce il file del database. Le pagine vengono segnate come libere per essere riutilizzate. Per recuperare spazio su disco, esegui VACUUM; (trattato nel capitolo sulle prestazioni).
  • Eliminare una riga non elimina le righe figlie in altre tabelle, a meno che ON DELETE CASCADE sia impostato e le chiavi esterne siano attive.
  • Un DELETE che non trova nessuna riga non è un errore. È un'istruzione riuscita con changes() = 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.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA