L'importazione CSV sta nella CLI, non nell'SQL
Nel dialetto SQL di SQLite non esiste un'istruzione IMPORT. Il caricamento dei CSV è una funzionalità della shell da riga di comando sqlite3: un dot-command chiamato .import. È un cambio di prospettiva importante se arrivi da LOAD DATA INFILE di MySQL o da COPY di Postgres: quelli girano sul server, mentre .import è qualcosa che fa per te lo strumento client, leggendo il file ed eseguendo istruzioni INSERT dietro le quinte.
Quindi tutto quello che vedi in questa pagina presuppone che tu sia dentro la shell sqlite3:
sqlite3 mydata.db
Se invece devi importare dal codice di un'applicazione (Python, Node, Go), leggerai il CSV nel tuo linguaggio e userai istruzioni INSERT parametrizzate. Vedremo questo approccio nel capitolo sull'integrazione con le applicazioni. Qui ci concentriamo sulla CLI.
Il .import di base
La strada più breve: dici a SQLite che il file è un CSV, poi punti .import sul file e su un nome di tabella.
.mode csv
.import people.csv people
Succedono due cose diverse a seconda che people esista già o no:
- La tabella non esiste: SQLite la crea, usando la prima riga del CSV come nomi delle colonne. Ogni colonna ha affinità
TEXT. - La tabella esiste: SQLite inserisce ogni riga del file come dato. L'eventuale riga di intestazione diventa una riga.
È nel secondo caso che quasi tutti cadono al primo tentativo. Se il tuo CSV ha un'intestazione e la tabella esiste già, devi saltarla in modo esplicito.
Saltare l'intestazione su una tabella esistente
Usa --skip 1 per dire a .import di ignorare le prime N righe:
CREATE TABLE people (
name TEXT,
age INTEGER,
city TEXT
);
.import --csv --skip 1 people.csv people
--csv è una scorciatoia per .mode csv limitata a questo solo comando, quindi non devi impostare la modalità a parte. --skip 1 scarta l'intestazione. Le righe restanti vengono inserite in people nell'ordine delle colonne.
Un controllo veloce dopo l'importazione:
SELECT count(*) FROM people;
SELECT * FROM people LIMIT 5;
L'ordine delle colonne nel file deve corrispondere all'ordine delle colonne nella tabella. Non c'è una mappatura basata sull'intestazione: .import si limita ad allineare l'N-esimo campo con l'N-esima colonna.
Lasciare che SQLite crei la tabella per te
Per il lavoro esplorativo, la strada più semplice è saltare del tutto CREATE TABLE e lasciare che .import costruisca la tabella dall'intestazione:
.mode csv
.import sales.csv sales
.schema sales
.schema sales mostrerà qualcosa del genere:
CREATE TABLE sales(
"order_id" TEXT,
"amount" TEXT,
"ordered_at" TEXT
);
Nota che ogni colonna è TEXT. È voluto: .import non prova a dedurre i tipi. Se vuoi amount come numero reale e ordered_at come timestamp vero e proprio, crea prima tu la tabella con i tipi giusti, poi importa con --skip 1. L'affinità di tipo di SQLite convertirà le stringhe numeriche in interi e reali all'inserimento.
Separatori personalizzati: TSV, pipe, punto e virgola
.mode csv usa la virgola. Per i file separati da tabulazioni, cambia modalità:
.mode tabs
.import data.tsv events
Per altri separatori, usa .separator dopo aver scelto una modalità:
.mode csv
.separator "|"
.import pipe_data.txt events
Una cosa da sapere: .mode csv segue le regole di quoting della RFC 4180, quindi i campi con virgole o a capo interni funzionano purché siano correttamente racchiusi tra ". .mode tabs è una modalità più semplice che divide su un carattere, senza quoting. Se il tuo file ha campi tra virgolette con separatori interni, resta in .mode csv e cambia il separatore. È il caso tipico dei CSV esportati da Excel con impostazioni italiane, che usano il punto e virgola.
Un esempio realistico
Supponiamo che orders.csv sia così:
order_id,customer,amount,ordered_at
1001,Ada,49.99,2026-01-12
1002,Boris,12.50,2026-01-13
1003,"Chen, Wei",199.00,2026-01-14
Nota che la riga 3 ha una virgola dentro un campo tra virgolette. Ecco la sessione completa:
In una shell vera, il blocco INSERT sarebbe sostituito da un unico .import --csv --skip 1 orders.csv orders. Il campo "Chen, Wei" resta intatto perché la modalità CSV rispetta le virgolette. Grazie ai tipi delle colonne, amount diventa un numero reale e order_id un intero.
Racchiudere l'importazione in una transazione
.import esegue un INSERT per riga. Per qualche migliaio di righe va bene. Per un milione è lentissimo, a meno che tu non racchiuda tutto in una transazione, così SQLite non fa un commit dopo ogni riga:
BEGIN;
.import --csv --skip 1 big_file.csv events
COMMIT;
Questa sola modifica può trasformare un'importazione di diversi minuti in pochi secondi. Se qualcosa fallisce a metà, ROLLBACK annulla il caricamento parziale, il che è comodo anche per riprovare.
Puoi accelerare ancora eliminando gli indici prima dell'importazione e ricreandoli dopo: la manutenzione degli indici riga per riga si fa sentire.
Errori comuni e come risolverli
Error: expected N columns but found M: il numero di campi di una riga non corrisponde alla tabella. Di solito:
- Una virgola di troppo in un campo senza virgolette. Riesporta con un quoting CSV corretto, oppure passa a
.mode csv(RFC 4180) invece di.mode tabs. - Una riga vuota alla fine del file. Modifica il file o usa
--skipin modo creativo. - La tabella ha più colonne del CSV. Aggiungi le colonne mancanti al file, oppure inserisci in una tabella di appoggio con la forma giusta e copia nella tabella vera.
L'intestazione compare come dato: hai dimenticato --skip 1 su una tabella esistente. Elimina quella riga (DELETE FROM t WHERE rowid = 1) e riesegui con il flag.
Numeri salvati come stringhe: hai lasciato che .import creasse la tabella, quindi ogni colonna è TEXT. Elimina la tabella, definiscila in modo esplicito con colonne INTEGER/REAL e reimporta.
Error: no such file: il percorso è relativo alla cartella da cui hai avviato sqlite3, non al file del database. Usa un percorso assoluto oppure fai cd nella cartella giusta prima di aprire la shell.
La CLI stampa i numeri di riga negli errori, ed è il modo più rapido per trovare la riga incriminata in un file grande.
Riepilogo veloce
.importè un dot-command della CLI, non SQL. Eseguilo dentro la shellsqlite3.- Usa
--csvper gestire correttamente le virgolette e--skip 1per ignorare una riga di intestazione. - Se la tabella non esiste,
.importla crea dall'intestazione, ma ogni colonna saràTEXT. Crea tu la tabella per avere i tipi giusti. - Racchiudi le importazioni grandi in
BEGIN/COMMITper evitare una transazione per riga. - L'ordine delle colonne nel file deve corrispondere all'ordine delle colonne nella tabella.
Prossimo passo: esportare i dati
Importare è metà del lavoro. La stessa shell può riversare risultati di query o tabelle intere in CSV, JSON o SQL: utile per backup, pipeline di dati e per passare i dati ad altri strumenti. Lo vediamo nella pagina sull'esportazione dei dati.
Domande frequenti
Come importo un file CSV in SQLite?
Apri il database con la CLI sqlite3, passa alla modalità CSV con .mode csv, poi esegui .import data.csv table_name. Se la tabella non esiste ancora, SQLite la crea usando la prima riga del file come nomi delle colonne. Se esiste già, ogni riga del file viene inserita come dato, quindi di solito ti serve .import --skip 1 per saltare l'intestazione.
Come importo un CSV con intestazione in una tabella SQLite esistente?
Usa .import --csv --skip 1 data.csv table_name. Il flag --skip 1 dice a SQLite di ignorare la prima riga, così l'intestazione non diventa una riga di dati. Senza, ti ritroverai con una riga che contiene letteralmente i nomi delle colonne.
Perché l'importazione CSV in SQLite fallisce con 'expected N columns but found M'?
Il file ha righe con un numero di colonne diverso da quello della tabella, di solito per via di virgole interne, virgolette non gestite o una riga vuota finale. Usa .mode csv (o --csv) invece di .mode tabs, così SQLite gestisce le virgolette secondo la RFC 4180, e controlla il file con un editor di testo per trovare separatori fuori posto. La CLI stampa il numero della riga incriminata, che è il modo più rapido per trovarla.