◈ STORAGE CLASS
SQLite usa un sistema di tipi dinamico. Il tipo appartiene al valore, non alla colonna. Esistono 5 storage class native.
LE 5 CLASSI
NULL -- valore assente / sconosciuto INTEGER -- intero con segno, 1-8 byte -- a seconda del valore REAL -- float IEEE 754 a 8 byte TEXT -- stringa UTF-8 / UTF-16 BLOB -- dati binari raw -- SQLite NON ha tipi BOOLEAN o DATE -- BOOLEAN → INTEGER (0/1) -- DATE → TEXT o INTEGER o REAL
ORDINE DI CONFRONTO
-- NULL < INTEGER < REAL < TEXT < BLOB SELECT NULL < 0; → NULL SELECT NULL IS NULL; → 1 SELECT 1 < 'a'; → 1 (integer < text) SELECT 'a' < x'61'; → 1 (text < blob)
TYPEOF() — VERIFICARE IL TIPO
typeof(42) → 'integer' typeof(3.14) → 'real' typeof('ciao') → 'text' typeof(x'DEADBEEF') → 'blob' typeof(NULL) → 'null' -- attenzione: typeof(1.0) → 'real' (non integer!) typeof('3.14') → 'text' (non real!) typeof('') → 'text' (stringa vuota) -- filtro per tipo in WHERE SELECT * FROM t WHERE typeof(col) = 'integer';
BLOB LITERAL
-- notazione esadecimale: x'...' SELECT x'AABB'; → blob 2 byte SELECT length(x'AABB'); → 2 SELECT hex(x'41'); → '41' SELECT hex('A'); → '41'
RANGE DEI VALORI
-- INTEGER: da -9223372036854775808 a 9223372036854775807 (64 bit con segno) -- REAL: IEEE 754, min positivo ~5e-324, max ~1.8e+308 -- TEXT: dimensione limitata solo da SQLITE_MAX_LENGTH (default 1 GB) -- BLOB: idem, max 1 GB
SQLite NON supporta Inf e NaN. La divisione per zero restituisce NULL, non un errore.
◈ TYPE AFFINITY
La type affinity determina come SQLite tenta di convertire i valori inseriti in una colonna. Il tipo dichiarato nella CREATE TABLE viene mappato a una delle 5 affinita secondo queste regole (in ordine di priorita):
| Affinita | Regola sul nome del tipo dichiarato | Esempi di dichiarazione |
|---|---|---|
| INTEGER | Contiene "INT" (qualsiasi posizione) | INTEGER, INT, TINYINT, BIGINT, INT8 |
| TEXT | Contiene "CHAR", "CLOB" o "TEXT" | TEXT, VARCHAR(255), NCHAR, CLOB |
| BLOB / NONE | Tipo vuoto o non corrisponde a nessuna regola | (nessun tipo), BLOB |
| REAL | Contiene "REAL", "FLOA" o "DOUB" | REAL, FLOAT, DOUBLE, DOUBLE PRECISION |
| NUMERIC | Tutti gli altri casi | NUMERIC, DECIMAL(10,5), BOOLEAN, DATE |
Le regole si applicano in ordine: se il tipo contiene "INT", vince INTEGER anche se contiene anche "CHAR" (es.
BIGINT CHARACTER → INTEGER).COMPORTAMENTO PER INSERIMENTO
-- Affinita INTEGER: converte testo → intero se possibile col_int = '42' → memorizzato come INTEGER 42 col_int = 3.0 → memorizzato come INTEGER 3 col_int = 3.14 → memorizzato come REAL 3.14 (non intero esatto) -- Affinita TEXT: converte tutto in testo col_text = 3.14 → memorizzato come TEXT '3.14' -- Affinita NUMERIC: testo → numero se possibile, -- poi REAL → INTEGER se senza perdita col_num = '3.0' → memorizzato come INTEGER 3 col_num = '3.14' → memorizzato come REAL 3.14 col_num = 'abc' → memorizzato come TEXT 'abc' -- Affinita REAL: converte INTEGER in REAL col_real = 42 → memorizzato come REAL 42.0 -- Affinita NONE (BLOB): nessuna conversione col_blob = 'ciao' → memorizzato come TEXT 'ciao' (invariato)
CONVERSIONI IMPLICITE NEI CONFRONTI
-- Senza affinita di colonna, SQLite non converte SELECT 1 = '1'; → 0 (tipi diversi!) SELECT 1 = 1.0; → 1 (integer ≈ real) SELECT '1' = '1'; → 1 -- Con affinita di colonna INTEGER: -- il valore '42' viene convertito in 42 -- poi il confronto avviene tra interi WHERE col_integer = '42'; → funziona!
◈ CAST & CONVERSIONI
CAST DI BASE
CAST('42' AS INTEGER) → 42 CAST('3.14' AS REAL) → 3.14 CAST(42 AS TEXT) → '42' CAST(3.99 AS INTEGER) → 3 (troncato!) CAST(NULL AS INTEGER) → NULL CAST('abc' AS INTEGER) → 0 CAST('3abc' AS INTEGER) → 3 (legge fino a non-cifra) CAST('' AS INTEGER) → 0
CAST VERSO BLOB
CAST('hello' AS BLOB) → blob con byte UTF-8 di 'hello' CAST(x'68656C6C6F' AS TEXT) → 'hello' CAST(65 AS BLOB) → 8 byte (rappresentazione interna) -- per convertire numero → hex ASCII: SELECT hex(65); → '41'
◈ NULL
SQLite implementa la logica a tre valori (TRUE, FALSE, NULL). NULL non e uguale a niente, nemmeno a se stesso.
LOGICA A TRE VALORI
NULL = NULL → NULL (non TRUE!) NULL != NULL → NULL NULL > 0 → NULL -- operatori corretti per NULL col IS NULL → TRUE se NULL col IS NOT NULL → TRUE se non NULL -- AND con NULL TRUE AND NULL → NULL FALSE AND NULL → FALSE (short-circuit) -- OR con NULL TRUE OR NULL → TRUE (short-circuit) FALSE OR NULL → NULL
NULL NELLE FUNZIONI
-- la maggior parte delle funzioni -- restituisce NULL se l'input e NULL upper(NULL) → NULL length(NULL) → NULL abs(NULL) → NULL 42 + NULL → NULL -- COUNT(*) conta tutte le righe -- COUNT(col) ignora le righe con NULL COUNT(*) → conta tutto COUNT(col) → esclude NULL SUM(col) → ignora NULL AVG(col) → ignora NULL
GESTIRE NULL
COALESCE(a, b, c, 'default') → primo valore non-NULL IFNULL(col, 0) → valore alternativo se NULL NULLIF(a, b) → NULL se a = b, altrimenti a IIF(col IS NULL, 'vuoto', col) → ternario (≥ 3.32) -- NULL nell'ordinamento ORDER BY col ASC NULLS LAST -- ≥ 3.30 ORDER BY col DESC NULLS FIRST
◈ BOOLEANI & DATE
BOOLEANI
-- SQLite NON ha tipo BOOLEAN nativo -- usa INTEGER: 0 = false, qualsiasi ≠ 0 = true attivo INTEGER -- convenzione 0/1 WHERE attivo → attivo ≠ 0 WHERE attivo = 1 → esplicito WHERE attivo = TRUE → TRUE = 1 WHERE NOT attivo → attivo = 0 -- TRUE/FALSE sono alias di 1/0 (≥ 3.23) SELECT TRUE; → 1 SELECT FALSE; → 0
DATE E ORE
-- SQLite NON ha tipo DATE nativo -- si usa TEXT, REAL o INTEGER -- TEXT (ISO 8601) — raccomandato '2025-01-15' '2025-01-15 14:30:00' '2025-01-15T14:30:00Z' -- REAL (Julian Day Number) julianday('2025-01-15') → 2460690.5 -- INTEGER (Unix timestamp) strftime('%s', 'now') → secondi da epoch -- confronto date in TEXT funziona! WHERE data BETWEEN '2025-01-01' AND '2025-12-31'
◈ DDL — DEFINIZIONE DELLA STRUTTURA
CREATE TABLE
CREATE TABLE utenti ( id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT NOT NULL, email TEXT UNIQUE, eta INTEGER DEFAULT 0, creato TEXT DEFAULT (datetime('now')) ); -- se non esiste gia CREATE TABLE IF NOT EXISTS log ( id INTEGER PRIMARY KEY, msg TEXT );
AUTOINCREMENT garantisce ID sempre crescenti, ma e leggermente piu lento perche usa la tabella interna
sqlite_sequence. Senza AUTOINCREMENT, SQLite riutilizza i rowid eliminati. Nella maggior parte dei casi, INTEGER PRIMARY KEY senza AUTOINCREMENT e sufficiente.ALTER TABLE
-- aggiunge colonna ALTER TABLE utenti ADD COLUMN telefono TEXT; -- rinomina colonna (≥ 3.25) ALTER TABLE utenti RENAME COLUMN eta TO anni; -- rinomina tabella ALTER TABLE utenti RENAME TO clienti; -- drop colonna (≥ 3.35) ALTER TABLE utenti DROP COLUMN telefono;
SQLite non supporta
ALTER TABLE ... MODIFY COLUMN ne ALTER TABLE ... ALTER COLUMN. Per cambiare il tipo o i vincoli di una colonna si deve ricreare la tabella.DROP
DROP TABLE IF EXISTS vecchie_righe; DROP VIEW IF EXISTS v_attivi; DROP INDEX IF EXISTS idx_email;
CREATE VIEW
CREATE VIEW v_attivi AS SELECT id, nome, email FROM utenti WHERE attivo = 1;
CREATE TRIGGER
CREATE TRIGGER trg_log_delete AFTER DELETE ON utenti BEGIN INSERT INTO log (msg) VALUES ('Eliminato: ' || OLD.nome); END; CREATE TRIGGER trg_validate_email BEFORE INSERT ON utenti WHEN NEW.email NOT LIKE '%@%.%' BEGIN SELECT RAISE(ABORT, 'Email non valida'); END;
◈ INDICI & VINCOLI
INDICI
-- indice semplice CREATE INDEX idx_nome ON utenti(nome); -- indice univoco CREATE UNIQUE INDEX idx_email ON utenti(email); -- indice composto CREATE INDEX idx_citta_nome ON utenti(citta, nome); -- indice parziale CREATE INDEX idx_attivi ON utenti(email) WHERE attivo = 1; -- indice su espressione CREATE INDEX idx_nome_lower ON utenti(lower(nome)); DROP INDEX IF EXISTS idx_nome;
VINCOLI
CREATE TABLE prodotti ( id INTEGER PRIMARY KEY, sku TEXT UNIQUE NOT NULL, prezzo REAL NOT NULL CHECK(prezzo > 0), cat_id INTEGER REFERENCES categorie(id) ON DELETE CASCADE ON UPDATE SET NULL, stock INTEGER DEFAULT 0 CHECK(stock >= 0) ); -- azioni foreign key: -- CASCADE, SET NULL, SET DEFAULT, -- RESTRICT, NO ACTION
FOREIGN KEY
-- !! disabilitato di default !! PRAGMA foreign_keys = ON; FOREIGN KEY(col) REFERENCES altra_tabella(col) ON DELETE CASCADE ON UPDATE RESTRICT
EXPLAIN QUERY PLAN
-- visualizza piano di esecuzione EXPLAIN QUERY PLAN SELECT * FROM utenti WHERE email = 'a@b.it'; -- bytecode della query EXPLAIN SELECT * FROM utenti;
◈ DML — MANIPOLAZIONE DEI DATI
INSERT
INSERT INTO utenti (nome, email) VALUES ('Mario', 'mario@x.it'); -- multi-riga INSERT INTO utenti (nome, email) VALUES ('Luigi', 'luigi@x.it'), ('Anna', 'anna@x.it'); -- da SELECT INSERT INTO archivio SELECT * FROM utenti WHERE anni > 60;
INSERT OR ... / UPSERT
-- ignora conflitti INSERT OR IGNORE INTO utenti (nome) VALUES ('Mario'); -- sovrascrive (delete + insert) INSERT OR REPLACE INTO utenti (id, nome) VALUES (1, 'Mario Rossi'); -- upsert con ON CONFLICT (≥ 3.24) INSERT INTO utenti (id, nome) VALUES (1, 'Mario') ON CONFLICT(id) DO UPDATE SET nome = excluded.nome;
UPDATE
UPDATE utenti SET nome = 'Mario Bianchi', anni = anni + 1 WHERE id = 1; -- con subquery UPDATE ordini SET totale = ( SELECT SUM(prezzo) FROM voci WHERE voci.ordine_id = ordini.id ); -- UPDATE con LIMIT (≥ 3.33, compilato con SQLITE_ENABLE_UPDATE_DELETE_LIMIT) UPDATE utenti SET attivo = 0 WHERE ultimo_accesso < '2024-01-01' LIMIT 100;
DELETE
DELETE FROM utenti WHERE id = 1; -- svuota tabella (mantiene struttura) DELETE FROM utenti; -- per resettare anche l'auto-increment: DELETE FROM utenti; DELETE FROM sqlite_sequence WHERE name = 'utenti';
RETURNING (≥ 3.35)
-- restituisce le righe modificate INSERT INTO utenti (nome) VALUES ('Mario') RETURNING id, nome; UPDATE utenti SET anni = anni + 1 WHERE id = 1 RETURNING *; DELETE FROM utenti WHERE attivo = 0 RETURNING id, nome;
◈ SELECT & JOIN
SELECT BASE
SELECT id, nome, anni FROM utenti WHERE anni BETWEEN 18 AND 65 AND email IS NOT NULL ORDER BY nome ASC LIMIT 10 OFFSET 20;
JOIN
-- INNER JOIN SELECT u.nome, o.totale FROM utenti u JOIN ordini o ON o.utente_id = u.id; -- LEFT JOIN SELECT u.nome, o.id FROM utenti u LEFT JOIN ordini o ON o.utente_id = u.id; -- RIGHT JOIN (≥ 3.39) / FULL OUTER JOIN (≥ 3.39) SELECT u.nome, o.id FROM utenti u FULL JOIN ordini o ON o.utente_id = u.id; -- CROSS JOIN (prodotto cartesiano) SELECT * FROM colori CROSS JOIN taglie;
CASE
SELECT nome, CASE WHEN anni < 18 THEN 'minore' WHEN anni < 65 THEN 'adulto' ELSE 'senior' END AS fascia FROM utenti;
◈ GROUP BY & AGGREGAZIONE
GROUP BY / HAVING
SELECT citta, COUNT(*) AS n, AVG(anni) AS eta_media FROM utenti GROUP BY citta HAVING COUNT(*) > 5 ORDER BY n DESC;
FUNZIONI DI AGGREGAZIONE
COUNT(*) / COUNT(col) -- * = tutte le righe, col = esclusi NULL SUM(col) / AVG(col) MAX(col) / MIN(col) GROUP_CONCAT(col, sep) -- concatena valori (default sep = ',') TOTAL(col) -- come SUM ma restituisce 0.0 invece di NULL
◈ SUBQUERY, CTE & WINDOW FUNCTIONS
CTE — COMMON TABLE EXPRESSION
WITH top_clienti AS ( SELECT utente_id, SUM(totale) spesa FROM ordini GROUP BY utente_id ) SELECT u.nome, t.spesa FROM top_clienti t JOIN utenti u ON u.id = t.utente_id ORDER BY t.spesa DESC;
CTE RICORSIVA
-- serie numerica WITH RECURSIVE nums(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM nums WHERE n < 10 ) SELECT * FROM nums; -- generare date consecutive WITH RECURSIVE cal(d) AS ( SELECT '2025-01-01' UNION ALL SELECT date(d, '+1 day') FROM cal WHERE d < '2025-01-31' ) SELECT * FROM cal;
SUBQUERY SCALARI & EXISTS
-- subquery scalare (restituisce 1 valore) SELECT nome, (SELECT COUNT(*) FROM ordini WHERE ordini.uid = utenti.id) n_ordini FROM utenti; -- EXISTS SELECT nome FROM utenti u WHERE EXISTS ( SELECT 1 FROM ordini o WHERE o.uid = u.id AND o.totale > 1000 );
WINDOW FUNCTIONS (≥ 3.25)
SELECT nome, ROW_NUMBER() OVER (ORDER BY spesa DESC) rn, RANK() OVER (ORDER BY spesa DESC) rk, DENSE_RANK() OVER (ORDER BY spesa DESC) dr, SUM(spesa) OVER () totale_globale, SUM(spesa) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) running_total, LAG(spesa) OVER (ORDER BY id) spesa_prec, LEAD(spesa) OVER (ORDER BY id) spesa_succ FROM clienti; -- con PARTITION BY SELECT citta, nome, ROW_NUMBER() OVER ( PARTITION BY citta ORDER BY spesa DESC ) pos_in_citta FROM clienti;
◈ UNION / INTERSECT / EXCEPT
SELECT email FROM utenti UNION -- unione senza duplicati SELECT email FROM invitati; SELECT email FROM utenti UNION ALL -- unione con duplicati (piu veloce) SELECT email FROM invitati; SELECT email FROM utenti INTERSECT -- comuni ad entrambi SELECT email FROM newsletter; SELECT email FROM utenti EXCEPT -- presenti solo nel primo SELECT email FROM ban;
◈ OPERATORI
ARITMETICI
+ addizione - sottrazione * moltiplicazione / divisione (intera se entrambi INTEGER) % modulo (resto) SELECT 7 / 2; → 3 (divisione intera!) SELECT 7.0 / 2; → 3.5 (almeno un REAL) SELECT 7 % 2; → 1 SELECT -7 % 2; → -1
CONFRONTO
= == uguale != <> diverso < <= > >= confronto IS uguale (gestisce NULL) IS NOT diverso (gestisce NULL) IS NULL / IS NOT NULL BETWEEN a AND b inclusivo IN (lista) NOT IN (lista)
LOGICI
AND -- entrambi veri OR -- almeno uno vero NOT -- negazione -- EXISTS WHERE EXISTS (SELECT 1 FROM ...) WHERE NOT EXISTS (...)
BITWISE & CONCATENAZIONE
-- bitwise (solo su INTEGER) & AND bit a bit | OR bit a bit ~ NOT bit a bit (unario) << shift sinistro >> shift destro SELECT 5 & 3; → 1 (101 & 011) SELECT 5 | 3; → 7 (101 | 011) SELECT 5 << 1; → 10 -- concatenazione stringa || concatena SELECT 'ciao' || ' ' || 'mondo'; → 'ciao mondo'
ESPRESSIONI UTILI
-- clamp (limita tra min e max) MAX(0, MIN(100, valore)) -- arrotondamento finanziario ROUND(prezzo * 1.22, 2) -- divisione sicura (evita div/0 → NULL) CASE WHEN denom = 0 THEN NULL ELSE num * 1.0 / denom END
◈ LIKE, GLOB & COLLATION
LIKE (CASE-INSENSITIVE ASCII)
WHERE nome LIKE 'Mar%' -- inizia con Mar WHERE nome LIKE '_ario' -- Mario, Dario... WHERE nome LIKE '%ciao%' -- contiene 'ciao' -- escape per carattere letterale WHERE col LIKE '50\%' ESCAPE '\' → cerca letteralmente '50%' -- LIKE e case-insensitive solo per ASCII -- per Unicode serve ICU extension
GLOB (CASE-SENSITIVE, STILE UNIX)
WHERE path GLOB '*.txt' -- finisce con .txt WHERE nome GLOB '[A-Z]*' -- inizia con maiuscola WHERE nome GLOB 'foto_???' -- foto_ + 3 char WHERE id IN (1, 2, 3) WHERE id NOT IN (SELECT id FROM ban)
COLLATION (ORDINAMENTO TESTO)
-- collation disponibili BINARY -- default, byte per byte NOCASE -- A-Z insensitive (solo ASCII) RTRIM -- ignora spazi a destra -- su colonna nome TEXT COLLATE NOCASE -- su query ORDER BY nome COLLATE NOCASE WHERE nome = 'mario' COLLATE NOCASE -- su indice CREATE INDEX idx_nome_ci ON utenti(nome COLLATE NOCASE);
REGEXP
-- REGEXP richiede un'estensione -- (non e built-in in SQLite) -- con estensione caricata: WHERE email REGEXP '^[a-z]+@.+\..+$'
◈ FUNZIONI — TESTO & NUMERI
TESTO
length(s) → n. caratteri upper(s) / lower(s) substr(s, start, len) → sottostringa (1-based) replace(s, da, a) trim(s) / ltrim(s) / rtrim(s) instr(s, pattern) → posizione (0 se assente) printf('%05d', n) → formattazione hex(blob) → rappresentazione hex quote(val) → escape SQL unicode(s) → codepoint primo char char(n1, n2, ...) → stringa da codepoint
NUMERI
abs(n) round(n, decimali) ceil(n) / floor(n) -- ≥ 3.35 max(a, b) / min(a, b) -- scalari, non aggregati random() → intero 64 bit casuale randomblob(n) → n byte casuali zeroblob(n) → n byte zero sign(n) → -1, 0, 1 (≥ 3.39)
TIPO & INFORMAZIONI
typeof(val) → 'integer','text'... sqlite_version() → es. '3.45.1' last_insert_rowid() changes() → righe modificate total_changes() → totale dalla connessione
◈ FUNZIONI — DATA E ORA
FUNZIONI BASE
date('now') → '2025-01-15' time('now') → '14:30:00' datetime('now') → '2025-01-15 14:30:00' julianday('now') → 2460690.1... unixepoch('now') → intero (≥ 3.38) strftime('%Y-%m', 'now')
MODIFICATORI
date('now', '+7 days') date('now', '-1 month') date('now', 'start of month') date('now', 'start of year') date('now', 'weekday 1') → prossimo lun datetime('now', 'localtime') datetime('now', '+2 hours', '-30 minutes') -- si possono concatenare piu modificatori date('now', 'start of month', '+1 month', '-1 day') → ultimo giorno del mese corrente
FORMATO STRFTIME
strftime('%Y', 'now') → '2025' (anno) strftime('%m', 'now') → '01' (mese) strftime('%d', 'now') → '15' (giorno) strftime('%H', 'now') → '14' (ore) strftime('%M', 'now') → '30' (minuti) strftime('%S', 'now') → '00' (secondi) strftime('%w', 'now') → '3' (giorno sett. 0=dom) strftime('%j', 'now') → '015' (giorno anno) strftime('%s', 'now') → unix timestamp strftime('%W', 'now') → '03' (settimana anno)
CALCOLI TRA DATE
-- differenza in giorni SELECT julianday('2025-12-31') - julianday('2025-01-01'); → 364.0 -- eta in anni SELECT (strftime('%Y', 'now') - strftime('%Y', data_nascita)) - (strftime('%m-%d', 'now') < strftime('%m-%d', data_nascita)) AS eta;
◈ FUNZIONI — NULL & CONDIZIONI
COALESCE(a, b, c) → primo valore non-NULL IFNULL(val, default) → val se non NULL, altrimenti default NULLIF(a, b) → NULL se a = b, altrimenti a IIF(cond, val_true, val_false) → ternario (≥ 3.32) -- CASE come funzione CASE col WHEN 'a' THEN 1 WHEN 'b' THEN 2 ELSE 0 END -- CASE con condizioni CASE WHEN prezzo > 100 THEN 'caro' WHEN prezzo > 10 THEN 'medio' ELSE 'economico' END
◈ FUNZIONI — JSON
Le funzioni JSON sono disponibili da SQLite 3.9.0. Gli operatori -> e ->> richiedono ≥ 3.38.
LETTURA & VALIDAZIONE
json('{"a":1}') → valida/normalizza json_extract(col, '$.nome') json_extract(col, '$.tag[0]') json_type(col, '$.x') → tipo del valore json_valid(col) → 0 o 1 -- operatori (≥ 3.38) col -> '$.nome' → valore JSON col ->> '$.nome' → valore SQL nativo
CREAZIONE & MODIFICA
json_array(1, 2, 3) → '[1,2,3]' json_object('k', 'v') → '{"k":"v"}' json_set(col, '$.x', 42) → aggiunge/modifica json_insert(col, '$.x', 42) → solo se non esiste json_replace(col, '$.x', 42)→ solo se esiste json_remove(col, '$.x') → rimuove chiave json_patch(base, patch) → RFC 7396 merge -- iterare su array JSON SELECT value FROM json_each('[1,2,3]'); → 1, 2, 3 (una riga per elemento)
◈ TRANSAZIONI
TIPI DI BEGIN
BEGIN; -- o BEGIN DEFERRED (default) BEGIN IMMEDIATE; -- lock in scrittura subito BEGIN EXCLUSIVE; -- lock totale sul database COMMIT; -- o END ROLLBACK;
ESEMPIO TRANSAZIONE
BEGIN; UPDATE conti SET saldo = saldo - 100 WHERE id = 1; UPDATE conti SET saldo = saldo + 100 WHERE id = 2; COMMIT;
SAVEPOINT
SAVEPOINT sp1; INSERT INTO ... SAVEPOINT sp2; UPDATE ... ROLLBACK TO sp2; -- annulla solo sp2 RELEASE sp2; -- rilascia savepoint RELEASE sp1; -- equivale a COMMIT
DEFERRED: lock acquisito alla prima lettura/scrittura.
IMMEDIATE: lock scrittura acquisito subito — previene deadlock in scenari concorrenti.
EXCLUSIVE: nessun altro puo leggere o scrivere.
IMMEDIATE: lock scrittura acquisito subito — previene deadlock in scenari concorrenti.
EXCLUSIVE: nessun altro puo leggere o scrivere.
◈ PRAGMA
PERFORMANCE
PRAGMA journal_mode = WAL; → Write-Ahead Log PRAGMA synchronous = NORMAL; → bilancia sicurezza/I/O PRAGMA cache_size = -64000; → ~64 MB di cache PRAGMA temp_store = MEMORY; PRAGMA mmap_size = 268435456;→ 256 MB mmap
INTEGRITA
PRAGMA foreign_keys = ON; PRAGMA integrity_check; → verifica DB completa PRAGMA quick_check; → verifica veloce PRAGMA foreign_key_check; PRAGMA wal_checkpoint(FULL);
INFORMAZIONI
PRAGMA table_info(utenti); → schema colonne PRAGMA table_xinfo(utenti); → include colonne nascoste PRAGMA index_list(utenti); → lista indici PRAGMA index_info(idx_nome); PRAGMA database_list; → db allegati PRAGMA compile_options; → opzioni compilazione PRAGMA page_count; PRAGMA page_size; PRAGMA freelist_count;
MANUTENZIONE
VACUUM; → compatta il file VACUUM INTO 'backup.db'; → copia compatta ANALYZE; → aggiorna statistiche REINDEX; → ricostruisce indici
WAL mode e consigliato per la maggior parte degli scenari: permette letture concorrenti durante la scrittura e migliora le performance. Impostare
journal_mode = WAL e synchronous = NORMAL all'apertura della connessione.◈ SHELL CLI (.sqlite3)
META-COMANDI
.open file.db -- apri/crea database .tables -- lista tabelle .schema utenti -- DDL della tabella .schema -- tutto lo schema .indexes utenti -- lista indici .fullschema -- schema + statistiche .dbinfo -- info sul database .quit / .exit
OUTPUT
.mode column -- colonne allineate .mode table -- tabella ASCII .mode csv .mode json .mode markdown .headers on .width 20 10 30 .output risultati.csv .once risultati.txt -- solo prossima query .output stdout
IMPORT / EXPORT
-- importa CSV .mode csv .import dati.csv nome_tabella -- esporta dump SQL .output dump.sql .dump .output stdout -- esegui script .read script.sql
DA TERMINALE
# apri e interroga sqlite3 file.db "SELECT * FROM utenti;" # esegui script sqlite3 file.db < script.sql # backup veloce sqlite3 orig.db ".backup backup.db" # export CSV sqlite3 -header -csv file.db \ "SELECT * FROM utenti;" > out.csv # ATTACH: collegare piu database sqlite3 main.db ATTACH 'altro.db' AS db2; SELECT * FROM db2.tabella;