Una connessione, tanti file
Una connessione SQLite non è legata a un solo file. Con ATTACH DATABASE puoi aprire altri file .db accanto a quello di partenza e interrogarli tutti come se fossero schemi di un unico database. È la cosa più vicina che SQLite abbia a "più database su un solo server".
La forma base:
Il file archive.db viene creato se non esiste, proprio come il database principale. Da qui in poi, in questa sessione, tutto ciò che ha il prefisso archive. vive in quel secondo file. Tutto ciò che ha il prefisso main. (o nessun prefisso) vive nell'originale.
La tua connessione ha sempre due schemi impliciti: main (il file che hai aperto per primo) e temp (uno spazio di lavoro per le tabelle temporanee). Collegare altri file ne aggiunge altri.
La sintassi e a cosa serve l'alias
ATTACH DATABASE 'path/to/file.db' AS alias_name;
L'alias è il nome di schema che userai per qualificare le tabelle. È locale alla connessione corrente: un'altra connessione che collega lo stesso file può scegliere un alias diverso. Scegline uno breve e descrittivo (archive, analytics, cache), perché lo scriverai spesso.
Alcune cose da sapere:
- Il percorso è relativo alla directory di lavoro del processo, a meno che non sia assoluto.
- La stringa
':memory:'collega un database in memoria nuovo con quell'alias. - L'alias non può coincidere con
mainotempe non può ripetersi tra un collegamento e l'altro.
JOIN tra database diversi
È la funzionalità per cui la maggior parte delle persone usa ATTACH. Quando due file sono nella stessa connessione, puoi fare il JOIN delle loro tabelle in un'unica query:
Il query planner tratta entrambi gli schemi come tratta le tabelle di main. Gli indici sulle tabelle collegate vengono usati. EXPLAIN QUERY PLAN funziona su entrambi. Non c'è nessun viaggio in rete: entrambi i file sono aperti nello stesso processo.
È davvero utile per separare i dati caldi dagli archivi freddi, tenere un file per ogni tenant o prendere dati di riferimento da un database di consultazione in sola lettura.
Collegamenti in sola lettura e in memoria
Se il secondo database è qualcosa che vuoi leggere ma mai modificare, per esempio un dataset di riferimento distribuito con l'app, collegalo in sola lettura con un URI:
La forma URI richiede che la libreria SQLite abbia SQLITE_OPEN_URI abilitato (lo è nella CLI e nella maggior parte dei binding per i vari linguaggi). Qualsiasi INSERT, UPDATE o DELETE su ref.* darà errore prima di toccare il file.
I collegamenti in memoria sono altrettanto comodi per preparare dati intermedi:
scratch sparisce quando la connessione si chiude. È come temp, ma la durata la decidi tu.
Le transazioni coprono tutti i database collegati
Un singolo BEGIN/COMMIT copre le scritture su main e su ogni schema collegato. O va tutto a buon fine, o si annulla tutto: l'atomicità è garantita anche tra file diversi:
Spostare righe da una tabella attiva a un file di archivio è proprio il tipo di operazione in cui vuoi questa garanzia. Senza atomicità tra file, un crash a metà ti lascerebbe con dei duplicati o, peggio, con righe perse.
Un'avvertenza: quando in una transazione si scrive su più di un database collegato, SQLite usa un protocollo di commit più prudente che richiede un journal temporaneo. È più lento dei commit su un solo file, ma resta sicuro.
Scollegare un database
Quando hai finito con un database collegato, rimuovilo:
DETACH DATABASE archive;
Il file resta intatto su disco: DETACH chiude solo l'handle nella connessione corrente. Due restrizioni da ricordare:
- Non puoi scollegare
mainotemp. - Non puoi scollegare un database che è dentro una transazione o che ha istruzioni aperte su di sé.
Se ti dimentichi di scollegarlo non è la fine del mondo: chiudere la connessione ripulisce tutto.
Limiti ed errori comuni
Alcuni limiti pratici da conoscere:
- Il limite predefinito è di 10 database collegati per connessione (più
mainetemp). Il massimo in fase di compilazione è 125. Se raggiungi il limite vedraitoo many attached databases - max 10. - Ogni file collegato usa una propria page cache. Collegare una dozzina di database grandi non è gratis: la RAM sale.
ATTACHdi per sé non può essere eseguito dentro una transazione. Eseguilo prima diBEGINo dopoCOMMIT.
Alcuni errori che probabilmente incontrerai:
-- Il file non esiste e la directory non è scrivibile:
Error: unable to open database: 'missing/path.db'
-- Hai provato a scrivere su un collegamento in sola lettura:
Error: attempt to write a readonly database
-- Hai usato lo stesso alias due volte:
Error: database archive is already in use
La maggior parte di questi errori è ovvia appena li leggi. Quello su "already in use" confonde parecchie persone: ATTACH non sostituisce un alias esistente, devi prima fare DETACH.
Uno schema realistico: separare dati caldi e freddi
Mettiamo tutto insieme: un piccolo flusso di archiviazione che sposta gli ordini più vecchi di un anno fuori dal database principale:
Le righe vecchie passano in archive.orders, quelle recenti restano in main. I report che hanno bisogno dello storico possono fare JOIN su entrambi; le query quotidiane su main.orders restano veloci perché la tabella è più piccola. Stessa connessione, due file, una transazione.
Prossimo passo: prepared statement
ATTACH serve a dare a una connessione accesso a più dati. Il prossimo gruppo di argomenti riguarda come le applicazioni parlano con SQLite in modo sicuro ed efficiente, a partire dai prepared statement, la base del binding dei parametri e delle query a prova di injection.
Domande frequenti
Cosa fa ATTACH DATABASE in SQLite?
ATTACH DATABASE 'file.db' AS alias apre un secondo file di database SQLite dentro la connessione corrente e gli assegna un nome di schema. Da lì in poi puoi riferirti alle sue tabelle come alias.table_name e fare JOIN con le tabelle del database principale in un'unica query.
Quanti database può collegare SQLite contemporaneamente?
Di default SQLite consente fino a 10 database collegati per connessione, oltre agli schemi main e temp. Il limite massimo è 125, configurabile in fase di compilazione con SQLITE_MAX_ATTACHED. Se superi il limite ottieni l'errore too many attached databases.
Posso interrogare più database SQLite collegati in una sola istruzione?
Sì. Una volta collegati, qualifica ogni tabella con il nome del suo schema: SELECT * FROM main.users JOIN archive.orders ON .... JOIN, subquery e INSERT ... SELECT funzionano tutti tra schemi diversi. Anche le transazioni coprono ogni database collegato, quindi un COMMIT è atomico su tutti i file.
Come scollego un database SQLite?
Esegui DETACH DATABASE alias. Il file resta intatto su disco: DETACH chiude solo l'handle nella connessione corrente. Non puoi scollegare main o temp, né un database che si trova nel mezzo di una transazione.