Importare CSV in SQLite con Python: Guida Pratica e Soluzioni agli 8 Errori Più Comuni

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
come importare CSV grandi in SQLite Python

È un classico: hai un file CSV pieno di dati, un database SQLite pronto a riceverli, e poche righe di codice Python per fare da ponte. Sembra facile, ma un attimo dopo ti ritrovi a fissare un messaggio d’errore come “database is locked” o, peggio, un SELECT COUNT(*) che restituisce un desolante “0”.

L’importazione di dati può trasformarsi rapidamente in un campo minato.

Prima di mettere in discussione le tue capacità (o lanciare il monitor fuori dalla finestra), sappi che questi problemi sono frequentissimi e hanno una soluzione logica.

In questa guida, non ci limitiamo a darti uno script funzionante, ma analizziamo gli 8 intoppi più comuni che trasformano un’operazione da 5 minuti in un incubo di debug, e vediamo esattamente come risolverli.

L’esempio include:

✅ Creazione del database
✅ Creazione delle tabelle
✅ Lettura dei CSV (anche molto grandi)
✅ Inserimento efficiente dei record
✅ Esempio di utilizzo finale

import csv
import sqlite3
import random
from datetime import datetime, timedelta

# ======================================================
# 1) GENERAZIONE CSV CLIENTI
# ======================================================

clienti = [
    [1, "Mario Rossi", "[email protected]"],
    [2, "Luca Bianchi", "[email protected]"],
    [3, "Giulia Verdi", "[email protected]"],
    [4, "Sofia Neri", "[email protected]"],
    [5, "Francesco Russo", "[email protected]"],
    [6, "Antonio Greco", "[email protected]"],
    [7, "Chiara Conti", "[email protected]"],
]

with open("clienti.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["cliente_id", "nome", "email"])
    writer.writerows(clienti)

print("✅ CSV clienti generato")


# ======================================================
# 2) GENERAZIONE CSV ORDINI (tante righe)
# ======================================================

NUM_ORDINI = 200   # << cambia questo per generare più ordini

prodotti = [
    ("Notebook", 799.99),
    ("Mouse Wireless", 19.90),
    ("Monitor 27\"", 189.50),
    ("Tastiera Meccanica", 79.90),
    ("Webcam HD", 49.99),
    ("Stampante Laser", 119.90),
    ("SSD 1TB", 89.90),
]

def random_date():
    start = datetime(2024, 1, 1)
    end = datetime(2024, 12, 31)
    delta = end - start
    return (start + timedelta(days=random.randint(0, delta.days))).strftime("%Y-%m-%d")

with open("ordini_vendita.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["order_id", "data_ordine", "cliente_id", "prodotto", "quantita", "prezzo_unitario"])

    for order_id in range(1, NUM_ORDINI + 1):
        cliente_id = random.choice(clienti)[0]
        prodotto, prezzo = random.choice(prodotti)
        quantita = random.randint(1, 5)

        writer.writerow([
            order_id,
            random_date(),
            cliente_id,
            prodotto,
            quantita,
            prezzo
        ])

print(f"✅ CSV ordini generato con {NUM_ORDINI} ordini")


# ======================================================
# 3) CREAZIONE DATABASE SQLITE
# ======================================================

conn = sqlite3.connect("vendite.db")
cursor = conn.cursor()

cursor.execute("""
CREATE TABLE IF NOT EXISTS clienti (
    cliente_id INTEGER PRIMARY KEY,
    nome TEXT,
    email TEXT
)
""")

cursor.execute("""
CREATE TABLE IF NOT EXISTS ordini (
    order_id INTEGER PRIMARY KEY,
    data_ordine TEXT,
    cliente_id INTEGER,
    prodotto TEXT,
    quantita INTEGER,
    prezzo_unitario REAL,
    FOREIGN KEY(cliente_id) REFERENCES clienti(cliente_id)
)
""")

print("✅ Tabelle SQLite create")


# ======================================================
# 4) IMPORT CSV CLIENTI
# ======================================================

with open("clienti.csv", "r", encoding="utf-8") as file:
    lettore = csv.reader(file)
    next(lettore)  # salta header

    cursor.executemany("""
        INSERT INTO clienti (cliente_id, nome, email)
        VALUES (?, ?, ?)
    """, lettore)

print("✅ Clienti importati")


# ======================================================
# 5) IMPORT CSV ORDINI
# ======================================================

with open("ordini_vendita.csv", "r", encoding="utf-8") as file:
    lettore = csv.reader(file)
    next(lettore)

    cursor.executemany("""
        INSERT INTO ordini (order_id, data_ordine, cliente_id, prodotto, quantita, prezzo_unitario)
        VALUES (?, ?, ?, ?, ?, ?)
    """, lettore)

print("✅ Ordini importati")

conn.commit()
conn.close()

print("🎉 Importazione completata! Database: vendite.db")
✅ CSV clienti generato
✅ CSV ordini generato con 200 ordini
✅ Tabelle SQLite create
✅ Clienti importati
✅ Ordini importati
🎉 Importazione completata! Database: vendite.db

Cointrolliamo quante righe ha scritto nel db:

import sqlite3

conn = sqlite3.connect("vendite.db")
cursor = conn.cursor()

cursor.execute("SELECT COUNT(*) FROM ordini")
print("Ordini:", cursor.fetchone()[0])

conn.close()
Ordini: 200

Controlla se la tabella CLIENTI contiene record

import sqlite3

conn = sqlite3.connect("vendite.db")
cursor = conn.cursor()

cursor.execute("SELECT COUNT(*) FROM clienti")
print("Clienti:", cursor.fetchone()[0])

conn.close()
Clienti: 7

Verifica i valori cliente_id dentro ORDINI

import sqlite3

conn = sqlite3.connect("vendite.db")
cursor = conn.cursor()

cursor.execute("SELECT order_id, cliente_id FROM ordini LIMIT 10")
print(cursor.fetchall())

conn.close()
[(1, 6), (2, 1), (3, 5), (4, 3), (5, 4), (6, 1), (7, 1), (8, 3), (9, 5), (10, 1)]

BONUS: Query per verificare i dati

Puoi eseguire questo snippet per stampare i primi ordini collegati ai clienti:

import sqlite3

conn = sqlite3.connect("vendite.db")
cursor = conn.cursor()

cursor.execute("""
SELECT o.order_id, o.data_ordine, c.nome, o.prodotto, o.quantita, o.prezzo_unitario
FROM ordini o
JOIN clienti c ON o.cliente_id = c.cliente_id
LIMIT 10
""")

for row in cursor.fetchall():
print(row)

conn.close()
(1, '2024-11-08', 'Antonio Greco', 'Webcam HD', 1, 49.99)
(2, '2024-11-21', 'Mario Rossi', 'Notebook', 3, 799.99)
(3, '2024-01-27', 'Francesco Russo', 'Notebook', 1, 799.99)
(4, '2024-01-25', 'Giulia Verdi', 'SSD 1TB', 1, 89.9)
(5, '2024-06-11', 'Sofia Neri', 'SSD 1TB', 2, 89.9)
(6, '2024-11-16', 'Mario Rossi', 'Webcam HD', 4, 49.99)
(7, '2024-07-04', 'Mario Rossi', 'SSD 1TB', 3, 89.9)
(8, '2024-09-25', 'Giulia Verdi', 'Notebook', 1, 799.99)
(9, '2024-01-17', 'Francesco Russo', 'SSD 1TB', 2, 89.9)
(10, '2024-10-25', 'Mario Rossi', 'Webcam HD', 2, 49.99)

Problemi comuni nell’importazione di CSV in SQLite con Python (e come risolverli)

Pubblicità

Quando inizi a importare CSV in SQLite con Python è normale incontrare errori, blocchi e comportamenti strani.
Ma dietro ogni errore c’è quasi sempre una piccola storia, uno scenario tipico… che probabilmente hai già vissuto.

Forse potrebbe interessarti anche:  First Mover vs Fast Follower: Come Misurare il Rischio Strategico con la Simulazione Monte Carlo

Di seguito trovi i problemi più frequenti spiegati come se fossi davvero lì, così capisci immediatamente cosa sta accadendo, perché e come risolverlo.

✅ 1. La query non restituisce risultati (ma tu sai che i dati ci sono)

Lo scenario

Hai appena importato 200 ordini.
Esegui la SELECT di test:

SELECT * FROM ordini LIMIT 10;

…e non esce niente. Vuoto cosmico.
E la tua prima reazione è: “Ma come? Ha importato 200 righe!”

Perché succede

Di solito è uno di questi casi:

  • Hai ricreato il database dopo l’importazione → quindi ora è vuoto.

  • Stai aprendo il file .db sbagliato (succede molto più spesso di quanto si pensi).

  • La JOIN non trova corrispondenze perché cliente_id non coincide tra clienti e ordini.

Come risolvere

Parti dai conteggi:

SELECT COUNT(*) FROM ordini;
SELECT COUNT(*) FROM clienti;

Poi controlla i valori:

SELECT DISTINCT cliente_id FROM ordini;
SELECT cliente_id FROM clienti;

Se non combaciano → reimporta i CSV.


✅ 2. Errore: “table X has no column named Y” (il classico colpo di scena)

Lo scenario

Hai modificato il CSV aggiungendo una colonna nuova.
Lanci l’import…
E SQLite ti dice che la colonna non esiste.
E tu pensi: “Ma come non esiste? Io l’ho appena creata!”

Perché succede

SQLite non ricrea una tabella se esiste già.
Tu hai cambiato lo script, ma la tabella è rimasta quella di prima, senza la nuova colonna.

Come risolvere

Tre strade:

  1. Nuke totale del database

    rm vendite.db
  2. Droppa solo la tabella

    DROP TABLE IF EXISTS ordini;
  3. Aggiungi la colonna

    ALTER TABLE ordini ADD COLUMN cliente_id INTEGER;

✅ 3. Errore: “database is locked” (l’incubo del multitasking)

Lo scenario

Lanci lo script Python per importare 200 ordini.
Lo script sembra bloccato, non va avanti.
Nel dubbio apri DB Browser for SQLite per “vedere se si sta muovendo qualcosa”.
Ed eccolo: database is locked.

Forse potrebbe interessarti anche:  Come funzionano i Motori di Ricerca: Guida Tecnica da Lucene a Elasticsearch

Ti sei appena auto-sabotato.

Perché succede

SQLite è piccolino: non ama avere troppi programmi che accedono al file contemporaneamente.

Casi tipici:

  • Python scrive… e DB Browser prova a leggere → si bloccano a vicenda.

  • Uno script precedente è crashato e non ha rilasciato il file.

  • Il database è in una cartella sincronizzata (Drive, Dropbox), che tenta di indicizzare il file mentre tu ci scrivi.

Come risolvere

✔ Soluzione rapida:
Chiudi tutto ciò che tocca il file .db prima di lanciare gli script.

✔ Soluzione Python:
Aggiungi un timeout:

conn = sqlite3.connect("vendite.db", timeout=10)

✔ Soluzione “pro”: WAL mode
Migliora tantissimo la concorrenza:

cursor.execute("PRAGMA journal_mode=WAL;")

✅ 4. JOIN vuota (quando la colpa non è della JOIN)

Lo scenario

Hai caricato clienti e ordini.
Scrivi una JOIN semplice:

SELECT * FROM ordini o
JOIN clienti c USING(cliente_id)

…ma il risultato è vuoto.

E pensi: “La JOIN non funziona”.
Spoiler: la JOIN funziona sempre.

Perché succede

Quasi sempre i cliente_id non combaciano.

Esempio tipico:

  • Ordini generati oggi

  • Clienti generati ieri

  • Qualcosa nel mezzo è cambiato (id, CSV rigenerati, database ripulito…)

Come risolvere

Confronta i valori reali:

SELECT DISTINCT cliente_id FROM ordini;
SELECT cliente_id FROM clienti;

Se non sono identici → devi reimportare.


✅ 5. CSV importato male (campi mancanti, encoding, colonne spostate)

Lo scenario

L’importazione va a buon fine…
ma poi dentro SQLite trovi:

  • nomi tagliati

  • colonne spostate

  • caratteri strani tipo “�”

Perché succede

Cause tipiche:

  • Il CSV non è in UTF-8

  • Il separatore non è una virgola

  • Il file è stato salvato con Excel (che spesso cambia encoding senza dirtelo)

Come risolvere

Apri il CSV con un editor affidabile (VS Code) e verifica:

  • UTF-8

  • Separatore

  • Header corretto

In Python usa sempre:

open("file.csv", encoding="utf-8")

✅ 6. Le foreign key non funzionano (e tu non capisci perché)

Lo scenario

Hai messo una foreign key perfetta.
Ma SQLite ti fa tranquillamente inserire un ordine con un cliente inesistente.

Perché succede

In SQLite… le foreign key sono disattivate di default.
Sì, davvero.

Come risolvere

Sempre prima di qualsiasi operazione:

PRAGMA foreign_keys = ON;

✅ 7. La tabella viene creata ma non popolata (e il CSV sembra ignorato)

Lo scenario

Esegui l’import.
Nessun errore.
Nessun warning.
Il mondo sembra bello.

Poi guardi la tabella: vuota.

Perché succede

Possibili colpevoli:

  • Percorso del file CSV errato

  • CSV vuoto

  • conn.commit() mancante

  • Header saltato due volte per errore

Forse potrebbe interessarti anche:  Excel e Python: Perché l'integrazione (e non la scelta) è il futuro della Data Analysis

Come risolvere

Controlla subito:

import os
print(os.path.exists("ordini_vendita.csv"))

Poi guarda cosa contiene:

with open("ordini_vendita.csv") as f:
print(f.read())

✅ 8. L’importazione è lenta (molto lenta)

Lo scenario

Stai importando 50.000 righe.
Lo script sembra finire domani.

Perché succede

SQLite diventa lento se:

  • inserisci riga per riga

  • fai commit ad ogni riga

  • non usi WAL

  • non usi transazioni

Come risolvere

✔ Usa executemany()
✔ Usa una singola transazione
✔ Abilita WAL
✔ Oppure usa Pandas:

df.to_sql("ordini", conn, if_exists="append", index=False)

Perché questo esercizio è interessante

Pubblicità

L’esempio di codice  è “interessante” perché illustra in modo implicito due concetti fondamentali nella gestione di grandi volumi di dati.

  1. Efficienza di executemany() vs. Loop: Molti principianti istintivamente scriverebbero un loop in Python per importare i dati:
    # MODO LENTO - NON FARE
    for row in lettore:
        cursor.execute("INSERT INTO... VALUES (?, ?, ?)", row)
    conn.commit()
    

    Questo è estremamente lento perché per ogni riga:

    • Python “parla” con SQLite.
    • SQLite esegue un singolo inserimento. Il  codice usa cursor.executemany(query, lettore). Qui, passa l’intero iteratore (il lettore CSV) direttamente al driver SQLite. È il driver stesso, ottimizzato in C, che gestisce il loop. La comunicazione Python-SQLite avviene una sola volta, riducendo l’overhead in modo drastico e rendendo l’importazione centinaia di volte più veloce.
  2. Gestione della Memoria (Streaming vs. In-Memory): Lo script può gestire un file ordini_vendita.csv da 500MB (o 5GB) usando la stessa identica (e minima) quantità di RAM che usa per 200 righe.
    • Perché? csv.reader(file) è un iteratore. Non carica l’intero file in memoria. Legge una riga dal disco, la passa a executemany, questa la inserisce nel DB, e poi il reader “dimentica” la riga e passa alla successiva.
    • L’approccio alternativo (e sbagliato per file grandi), come usare pandas.read_csv(), caricherebbe l’intero file CSV in RAM prima di iniziare a scrivere sul database.
    • Questo script è un esempio perfetto di processo di streaming (ETL): i dati fluiscono dal disco A (CSV) al disco B (DB) senza mai saturare la memoria di sistema.

Pubblicità