Vai al contenuto principale

DuckDB vs SQLite: un confronto completo tra database

Scopri le principali differenze tra DuckDB e SQLite e come si confrontano tra loro.
Aggiornato 29 set 2026  · 15 min leggi

Scopri con l'IA

ChatGPTClaudePerplexity

Nella gestione dei dati moderna, i database embedded leggeri svolgono un ruolo cruciale nell’alimentare applicazioni, workflow e analitiche. Due sistemi spesso messi a confronto sono DuckDB e SQLite. 

Entrambi sono database embedded, ma servono scopi diversi: SQLite eccelle nei carichi transazionali, mentre DuckDB è ottimizzato per query analitiche.

In questo articolo di confronto, vedremo come i due database differiscono tra loro.

In breve: DuckDB vs SQLite

Ecco una tabella di confronto con somiglianze e differenze tra DuckDB e SQLite. Per i dettagli completi, leggi le spiegazioni nel resto dell’articolo. 

Categoria SQLite DuckDB
Anno di rilascio 2000 2019
Scopo principale Database leggero, transazionale (OLTP) Database analitico embedded (OLAP)
Modello di storage Orientato alle righe Colonnare
Tipo di workload OLTP (insert, update, delete, point lookup) OLAP (aggregazioni, join, scan)
Esecuzione delle query Basata su iteratori (una riga alla volta) Vettorializzata (batch alla volta)
Punto di forza prestazionale Eccellente per transazioni piccole e frequenti Eccellente per query analitiche su grandi dataset
Modello di concorrenza Un solo writer, più reader Esecuzione parallela delle query su più core CPU
Gestione dimensione dati Ottimizzato per dataset piccoli e medi Gestisce dataset più grandi della memoria (esecuzione out-of-core)
Supporto formati file Solo file proprietario SQLite Supporto nativo per Parquet, Arrow e CSV
Accesso a dati esterni Richiede importazione prima delle query Query direttamente sui file senza import
Integrazioni Ampio supporto linguaggi (C, Python, Java, PHP, ecc.) Integrazione profonda per data science (Python, R, pandas, Jupyter)
Integrazione cloud Local-first, funzionalità cloud limitate Integrazione nativa con S3, Azure, GCS per Parquet/Arrow
Transazioni e ACID Pienamente conforme ad ACID ACID in un singolo processo, meno robusto per scritture concorrenti
Setup e dipendenze Zero configurazione, singola libreria C Installazione semplice ma con dipendenze opzionali (Arrow, Pandas)
Funzionalità SQL Focalizzato su CRUD; supporto analitico SQL parziale Solido supporto ANSI SQL con analitiche avanzate (CTE, funzioni finestra)
Casi d’uso tipici App mobile, dispositivi IoT, cache locali Data science, dashboard BI, pipeline ETL
Scalabilità Limitato a esecuzione single-thread Parallelismo multi-core e spill su disco per carichi elevati
Manutenzione Minima; occasionalmente VACUUM per lo spazio Minima; gestione Parquet partizionati per efficienza
Ideale per App embedded che necessitano di storage locale affidabile Workflow analitici in Python/R o contesti serverless
Contesto query di esempio SELECT * FROM users WHERE id=1; SELECT region, AVG(amount) FROM sales GROUP BY region;

Che cos’è SQLite?

SQLite è uno dei database più diffusi al mondo. Rilasciato nel 2000, è stato progettato come motore SQL leggero e serverless.

Ecco alcune caratteristiche chiave:

  • Autocontenuto e serverless
  • Setup a zero configurazione
  • Pienamente conforme ad ACID
  • Storage orientato alle righe

Grazie alla sua architettura leggera, può essere usato nei seguenti casi d’uso:

  • App mobile (Android, iOS)
  • Software desktop
  • Sistemi embedded come dispositivi IoT

Il punto di forza principale di SQLite è la semplicità e l’affidabilità. Gli sviluppatori possono distribuire un database basato su file insieme alla propria applicazione senza doversi preoccupare di dipendenze esterne.

Che cos’è DuckDB?

DuckDB è spesso definito lo SQLite per l’analisi. Introdotto nel 2019, è stato sviluppato al CWI. L’obiettivo principale nell’usare DuckDB era avere una soluzione OLAP in-process.

Ecco alcune caratteristiche chiave:

  • Formato di storage colonnare
  • Ottimizzato per workload OLAP
  • Esecuzione delle query vettorializzata
  • Design embedded in-process
  • Supporto diretto per formati file come Parquet e Arrow

Alcuni casi d’uso tipici includono:

  • Analisi interattive
  • Workflow di data science (Python, R, Jupyter)
  • Pipeline ETL leggere

Il punto chiave nell’usare DuckDB è che offre la potenza dei database analitici (come Snowflake o BigQuery) in un pacchetto embedded.

DuckDB vs SQLite: differenze e somiglianze chiave

1. Installazione e dipendenze

SQLite

SQLite è notoriamente privo di dipendenze. Si compila in un’unica libreria C che può essere inclusa praticamente in qualsiasi applicazione.

L’installazione è minima e può essere semplicemente scaricato dal sito di SQLite.

La portabilità è uno dei suoi maggiori punti di forza: funziona allo stesso modo su Windows, macOS, Linux, iOS e Android con quasi nessuno sforzo aggiuntivo.

DuckDB

DuckDB è anch’esso facile da installare, ma è distribuito come binari separati o pacchetti Python/R, invece di essere preinstallato universalmente.

Ha qualche dipendenza in più, soprattutto quando si abilitano integrazioni con Apache Arrow, Parquet o Pandas.

L’impronta resta ridotta, ma rispetto a SQLite, DuckDB richiede un po’ più di setup se vuoi funzionalità avanzate come connettori di storage cloud o integrazione con strumenti di data science.

2. Formati di storage e architettura

SQLite 

Memorizza i dati in formato orientato alle righe, ottimizzato per i task OLTP (Online Transaction Processing) come insert, update e delete.

Il recupero di una riga completa è veloce, ma le query analitiche che scansionano solo poche colonne su milioni di righe possono rallentare perché viene letto anche dato non necessario.

DuckDB

DuckDB usa un motore di storage colonnare, progettato appositamente per l’OLAP (Online Analytical Processing).

Il formato colonnare significa che le query che eseguono aggregazioni su grandi dataset (ad es., AVG(sales) o COUNT(*)) sono molto più rapide, perché solo le colonne rilevanti vengono caricate in memoria.

Consente anche una migliore compressione e un’esecuzione vettorializzata per maggior velocità.

Esempio: una query SELECT * FROM sales WHERE customer_id=123 è più rapida su SQLite, mentre SELECT AVG(amount) FROM sales GROUP BY region è molto più veloce su DuckDB.

3. Workload transazionali vs analitici

I due database differiscono anche per i workload:

  • OLTP (Transazionale): operazioni brevi e frequenti (es., app bancarie, profili utente, sistemi POS). Le priorità sono bassa latenza, integrità dei dati e accesso concorrente.
  • OLAP (Analitico): query complesse su grandi dataset (es., dashboard, strumenti BI, analisi ad hoc). Le priorità sono throughput, performance di scan e velocità di aggregazione.

SQLite

SQLite è ottimizzato per OLTP (insert, update, delete, point lookup).

Per esempio:

  • App mobile che archiviano dati offline.
  • Dispositivi IoT che registrano interazioni utente.
  • Cache locali in applicazioni desktop.

DuckDB

DuckDB è ottimizzato per OLAP (aggregazioni, join, scan).

Per esempio:

  • Data scientist che eseguono aggregazioni su file Parquet/CSV.
  • Dashboard BI interattive in cui le query sintetizzano milioni di righe.
  • Pipeline ETL/ELT che necessitano di trasformazioni in memoria leggere.

4. Supporto SQL e sintassi

Vediamo ora la sintassi SQL. Entrambi implementano un ampio sottoinsieme dello standard SQL con estensioni; nessuno è pienamente conforme all’ANSI.

SQLite

  • Supporta una larga parte dello standard SQL ma omette costrutti analitici avanzati.
  • Ottimo per operazioni CRUD e join più semplici.
  • Supporta funzioni finestra e CTE, ma manca di funzionalità analitiche più avanzate come GROUPING SETS e alcune estensioni di aggregazione.

Ecco come appare la sintassi:

CREATE TABLE sales (
    id INTEGER PRIMARY KEY,
    product TEXT,
    amount REAL,
    sale_date TEXT
);

DuckDB

  • Molto più vicino alla conformità ANSI SQL, con solido supporto per funzioni finestra, CTE, aggregazioni e operazioni insiemistiche.
  • Progettato per sembrare un PostgreSQL leggero per l’analisi.
  • L’integrazione con Arrow e Pandas consente di eseguire SQL direttamente su dataset esterni senza doverli prima importare.

Ecco un esempio simile di sintassi per DuckDB:

CREATE TABLE sales (
    id INTEGER,
    product VARCHAR,
    amount DOUBLE,
    sale_date DATE
);

SQLite vs DuckDB: architettura fondamentale e filosofia di design

Dal punto di vista architetturale, SQLite e DuckDB sono entrambi database embedded, in-process. Tuttavia, la loro filosofia progettuale è diversa. 

SQLite dà priorità a semplicità transazionale e affidabilità, mentre DuckDB è ottimizzato per workload analitici e l’integrazione con la data science moderna.

1. Architettura di storage

Innanzitutto, i due database differiscono nell’architettura di storage.

SQLite

SQLite è basato su righe, il che è ottimo per l’accesso transazionale. Significa che intere righe sono memorizzate insieme su disco.

Questo design avvantaggia i workload transazionali (OLTP) perché inserire, aggiornare o recuperare un singolo record richiede in genere l’accesso a una sola riga.

Applicazioni che necessitano accesso rapido e coerente a singoli record, come profili utente o dettagli d’ordine, funzionano in modo efficiente con questo modello.

DuckDB

DuckDB adotta un formato colonnare, progettato per scansionare rapidamente miliardi di righe. Il formato colonnare significa raggruppare i valori della stessa colonna su disco e in memoria.

Lo storage colonnare abilita query analitiche (OLAP) altamente efficienti, poiché aggregazioni, filtri e scan richiedono spesso di accedere solo a un sottoinsieme di colonne.

Le tecniche di compressione si applicano in modo più efficace a livello di colonna, riducendo l’uso di memoria e storage e migliorando le prestazioni di scan.

2. Metodologie di elaborazione delle query

Poi, vediamo come ciascuno processa le query.

In generale, il modello di SQLite è tarato su semplicità e correttezza transazionale, mentre il motore vettorializzato di DuckDB è progettato per throughput e performance analitiche.

SQLite

SQLite usa un modello tradizionale basato su iteratori, elaborando una riga alla volta lungo la pipeline di query.

Questo approccio row-at-a-time è leggero e adatto ai workload transazionali, dove le query spesso coinvolgono dataset piccoli e insert o update frequenti.

Tuttavia, emergono colli di bottiglia quando le query devono scansionare grandi dataset, poiché la valutazione riga per riga aggiunge overhead.

DuckDB

DuckDB impiega un’esecuzione vettorializzata delle query, elaborando batch di righe (vettori) alla volta anziché una per volta.

Questo approccio minimizza l’overhead della CPU, sfrutta meglio le cache dei processori moderni e consente il parallelismo SIMD (Single Instruction, Multiple Data).

L’esecuzione vettorializzata è particolarmente vantaggiosa per le analitiche: operazioni come aggregazioni, join e filtri risultano notevolmente più rapide su grandi volumi di dati.

In DuckDB, la seguente query beneficia di storage colonnare ed esecuzione vettorializzata, risultando sensibilmente più veloce.

SELECT region, AVG(amount)
FROM sales
GROUP BY region;

3. Architettura di database in-process

Infine, SQLite e DuckDB sono database embedded, cioè girano all’interno del processo dell’applicazione host anziché come server separato.

Questa architettura elimina la latenza di rete, semplifica il deployment e riduce l’onere operativo di gestire servizi esterni.

Questo li rende:

  • Facili da distribuire
  • Semplici da distribuire
  • A bassa latenza (niente overhead client-server)

SQLite 

SQLite ha introdotto l’idea di un database “serverless”. Significa che le tue applicazioni si collegano direttamente alla sua libreria e tutte le query vengono eseguite all’interno dell’app stessa.

DuckDB 

DuckDB segue un design in-process simile, ma applicato a contesti analitici.

Per evitare l’overhead di avviare motori analitici esterni, DuckDB sfrutta gli ambienti di data science (Python, R o anche notebook Jupyter) eseguendo le query al loro interno.

DuckDB vs SQLite: analisi delle prestazioni e benchmarking

Le prestazioni sono uno dei principali elementi distintivi tra DuckDB e SQLite. Pur essendo entrambi embedded, le priorità progettuali influenzano il comportamento sotto workload differenti. 

In generale, SQLite ha prestazioni elevate per casi d’uso transazionali, mentre DuckDB si comporta molto bene in contesti analitici.

1. Prestazioni delle query analitiche

DuckDB:

  • Supera SQLite per aggregazioni, join, group-by.
  • Può interrogare direttamente file Parquet/Arrow.
  • DuckDB è costruito per l’esecuzione di query analitiche e supera regolarmente SQLite nell’elaborazione di grandi dataset.

Vediamo come testarlo con una query di aggregazione.

Supponiamo di avere un dataset di vendite (sales.csv) con 10 milioni di righe contenenti order_id, customer_id, amount e date.

SQLite (Python):

Per eseguire questa query in SQLite con Python, dobbiamo caricare il CSV in un dataframe, scriverlo in una connessione SQLite3 e lanciare la query SQL su quel database.

Ecco un esempio di codice:

import sqlite3
import pandas as pd

# Load CSV into SQLite
df = pd.read_csv("sales.csv")
conn = sqlite3.connect("sales.db")
df.to_sql("sales", conn, if_exists="replace", index=False)

# Run a GROUP BY query directly on the imported table
cursor = conn.execute("""
    SELECT customer_id, SUM(amount) AS total_spent
    FROM sales
    GROUP BY customer_id
    ORDER BY total_spent DESC
    LIMIT 5
""")
print(cursor.fetchall())

DuckDB (Python):

Per eseguire una query simile con DuckDB in Python, i passaggi sono meno. Non serve un dataframe.

Ecco un esempio di codice:

import duckdb

con = duckdb.connect()
# Run a GROUP BY query directly on the CSV file
result = con.execute(""" SELECT customer_id, SUM(amount) AS total_spent FROM read_csv_auto('sales.csv') GROUP BY customer_id ORDER BY total_spent DESC LIMIT 5 """).fetchdf()
print(result)

2. Prestazioni nei workload transazionali

Per i workload transazionali, ciascun database ha casi d’uso specifici.

SQLite è ottimizzato per workload con molte transazioni e query puntuali. Gestisce frequenti operazioni INSERT, UPDATE e DELETE con overhead molto basso, risultando ideale per sistemi transazionali embedded.

SQLite supporta insert massivi in modo efficiente tramite transazioni e va molto bene quando sono richiesti aggiornamenti piccoli ma frequenti.

DuckDB, al contrario, si concentra su query analitiche dopo l’ingestione dei dati più che su aggiornamenti transazionali ad alta frequenza.

3. Scalabilità e utilizzo delle risorse

SQLite

SQLite è leggero e spesso usato per dataset piccoli o medi. Una singola query è eseguita su un solo thread e SQLite usa un modello a singolo writer con letture concorrenti; non parallelizza una singola SELECT, il che può limitare il throughput analitico.

DuckDB

DuckDB supporta il parallelismo multi-core e query più grandi della memoria. Scala sfruttando l’esecuzione parallela e può fare spill su disco quando necessario, abilitando analitiche out-of-core

Questo gli consente di analizzare dataset molto più grandi della memoria di sistema mantenendo prestazioni vicine a quelle in memoria.

4. Formati di file e performance I/O

SQLite

SQLite memorizza i dati nel suo formato proprietario orientato alle righe, portabile e stabile ma privo di interoperabilità diretta con formati analitici moderni. Spesso è necessario importare i dati in SQLite prima di eseguire query, aggiungendo overhead I/O.

DuckDB

DuckDB supporta nativamente più formati, inclusi Parquet, Arrow e CSV, permettendo di interrogare i dati direttamente senza conversione (per esempio, SELECT … FROM 'file.parquet').

Questo elimina l’overhead ETL e accelera l’analisi leggendo solo i dati richiesti da formati colonnari come Parquet.

Ingestione dati e interoperabilità in DuckDB vs SQLite

I database sono comunemente usati per ingestire grandi quantità di dati. Vediamo ora come se la cavano in termini di capacità di ingestione e interoperabilità.

1. Formati di file supportati e ingestione

DuckDB

DuckDB offre ingestione nativa di Parquet, Arrow e CSV. Può eseguire query SQL direttamente su Parquet senza importarlo in un file di database.

import duckdb

con = duckdb.connect()
result = con.execute("""
    SELECT customer_id, SUM(amount) AS total_spent
    FROM 'sales.parquet'
    GROUP BY customer_id
    ORDER BY total_spent DESC
    LIMIT 5
""").fetchdf()

print(result)

SQLite

SQLite richiede l’importazione dei dati esterni nel proprio formato di storage prima di poter eseguire query.

import sqlite3
import pandas as pd

# Load CSV into pandas first
df = pd.read_csv("sales.csv")

# Store into SQLite
conn = sqlite3.connect("sales.db")
df.to_sql("sales", conn, if_exists="replace", index=False)

# Query the imported table
cursor = conn.execute("""
    SELECT customer_id, SUM(amount) AS total_spent
    FROM sales
    GROUP BY customer_id
    ORDER BY total_spent DESC
    LIMIT 5
""")

print(cursor.fetchall())

2. Integrazione con strumenti di data science

DuckDB

DuckDB ha un’integrazione stretta con pandas, R e Jupyter, e si integra direttamente con i DataFrame pandas, permettendoti di trattarli come tabelle SQL:

import duckdb
import pandas as pd

# Example pandas DataFrame
df = pd.DataFrame({
    "customer_id": [1, 2, 1, 3],
    "amount": [100, 200, 150, 300]
})

# Query DataFrame directly with DuckDB
result = duckdb.query("""
    SELECT customer_id, AVG(amount) as avg_spent
    FROM df
    GROUP BY customer_id
""").to_df()

print(result)

SQLite

SQLite funziona con molti linguaggi di programmazione, ma è meno incentrato sulla data science.

Anche SQLite può interagire con pandas, ma in genere richiede più boilerplate.

import sqlite3
import pandas as pd

conn = sqlite3.connect(":memory:")
df = pd.DataFrame({
    "customer_id": [1, 2, 1, 3],
    "amount": [100, 200, 150, 300]
})

df.to_sql("sales", conn, index=False, if_exists="replace")

# Query back into pandas
query = "SELECT customer_id, AVG(amount) as avg_spent FROM sales GROUP BY customer_id"
result = pd.read_sql_query(query, conn)

print(result)

3. Binding di linguaggio e API

SQLite

SQLite esiste dal 2000, quindi i suoi binding sono estremamente maturi. Essendo solo una piccola libreria C, la maggior parte dei linguaggi ha supporto ufficiale o della community.

Alcuni esempi includono: C, Python (sqlite3), Java, PHP e altri.

DuckDB

DuckDB è molto più giovane, ma progettato con in mente workflow di data science + analytics. I suoi binding sono meno numerosi ma molto orientati alla data science.

Alcuni esempi includono: Python, R, C++, binding JavaScript.

Casi d’uso e domini applicativi

Sebbene siano entrambi database leggeri ed embedded, SQLite e DuckDB servono scopi molto diversi. 

SQLite prospera in contesti transazionali a livello applicativo, mentre DuckDB è ottimizzato per workload analitici in data science e business intelligence. In alcuni casi, possono persino completarsi a vicenda.

Vediamo alcuni esempi specifici qui sotto:

1. Scenari applicativi di SQLite

SQLite è estremamente leggero, il che lo rende perfetto per:

  • Storage per app mobile: SQLite è il database predefinito per app Android e iOS, offrendo storage locale persistente senza richiedere un backend server.
  • Dispositivi IoT: i dispositivi IoT leggeri spesso si affidano a SQLite per la sua impronta minima e l’assenza di configurazione.
  • Caching nel browser: SQLite è comunemente usato come livello di cache in sistemi distribuiti o come archivio intermedio durante il trasferimento di dati tra sistemi.

2. Applicazioni analitiche di DuckDB

DuckDB è strettamente legato a workflow analitici come pipeline ETL. Ecco un esempio di implementazione in Python.

Usando la CLI di DuckDB:

-- Connect / create DB
ATTACH 'warehouse.duckdb' AS wh; USE wh;

-- EXTRACT: read a CSV
CREATE OR REPLACE VIEW v_orders AS
SELECT * FROM read_csv_auto('data/orders.csv');  -- order_id, customer_id, order_ts, status, amount

-- TRANSFORM: clean types + derive metrics
WITH cleaned AS (
  SELECT
    CAST(order_id AS BIGINT)              AS order_id,
    CAST(customer_id AS BIGINT)           AS customer_id,
    TRY_CAST(order_ts AS TIMESTAMP)       AS order_ts,
    COALESCE(NULLIF(status,''),'unknown') AS status,
    TRY_CAST(amount AS DOUBLE)            AS amount
  FROM v_orders
),
agg AS (
  SELECT DATE_TRUNC('day', order_ts) AS order_date,
         SUM(amount)                 AS daily_gmv,
         COUNT(*)                    AS orders
  FROM cleaned
  GROUP BY 1
)
-- LOAD: upsert curated tables + export parquet
CREATE TABLE IF NOT EXISTS fact_orders AS SELECT * FROM cleaned WHERE 1=0;
MERGE INTO fact_orders t USING cleaned s
ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET
  customer_id=s.customer_id, order_ts=s.order_ts, status=s.status, amount=s.amount
WHEN NOT MATCHED THEN INSERT VALUES
  (s.order_id, s.customer_id, s.order_ts, s.status, s.amount);

COPY agg TO 'warehouse/daily_gmv' (FORMAT PARQUET, PARTITION_BY (order_date), OVERWRITE_OR_IGNORE 1);

Ora esegui il file nel terminale:

duckdb -c ".read etl.sql"

In alternativa, puoi usare DuckDB anche in Python per un workflow di analisi dati più snello.

import duckdb, pathlib
db = pathlib.Path("warehouse.duckdb")
con = duckdb.connect(str(db))

# EXTRACT
con.execute("""
CREATE OR REPLACE VIEW v_orders AS
SELECT * FROM read_csv_auto('data/orders.csv')
""")

# TRANSFORM
con.execute("""
CREATE OR REPLACE VIEW v_clean AS
SELECT CAST(order_id AS BIGINT) order_id,
       CAST(customer_id AS BIGINT) customer_id,
       TRY_CAST(order_ts AS TIMESTAMP) order_ts,
       COALESCE(NULLIF(status,''),'unknown') status,
       TRY_CAST(amount AS DOUBLE) amount
FROM v_orders
""")

# LOAD (table + parquet export)
con.execute("CREATE TABLE IF NOT EXISTS fact_orders AS SELECT * FROM v_clean WHERE 1=0")
con.execute("""
MERGE INTO fact_orders t USING v_clean s
ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET customer_id=s.customer_id, order_ts=s.order_ts, status=s.status, amount=s.amount
WHEN NOT MATCHED THEN INSERT VALUES (s.order_id, s.customer_id, s.order_ts, s.status, s.amount)
""")
con.execute("""
COPY (SELECT DATE_TRUNC('day', order_ts) AS order_date, SUM(amount) AS daily_gmv FROM fact_orders GROUP BY 1)
TO 'warehouse/daily_gmv' (FORMAT PARQUET, PARTITION_BY (order_date), OVERWRITE_OR_IGNORE 1)
""")

3. Scenari ibridi e complementari

Analizzare dati SQLite in DuckDB:

DuckDB può interrogare direttamente i dati dentro un file di database SQLite:

import duckdb

# Query a SQLite database via the sqlite_scanner extension
con = duckdb.connect()
con.install_extension("sqlite_scanner")
con.load_extension("sqlite_scanner")

result = con.execute("""
    SELECT customer_id, COUNT(*) AS orders
    FROM sqlite_scan('app_cache.db', 'orders')
    GROUP BY customer_id
    ORDER BY orders DESC
""").fetchdf()

print(result)

Ottimizzazione delle prestazioni e best practice

Sia SQLite che DuckDB sono efficienti per design, ma le prestazioni possono variare molto a seconda di come i dati vengono ingestiti, interrogati e mantenuti.

1. Insert massivi e gestione efficiente dei dati

SQLite

Per carichi dati elevati, usa sempre le transazioni. Inserire riga per riga senza transazione costringe SQLite a fare commit per ogni riga, rallentando drasticamente.

import sqlite3

conn = sqlite3.connect("perf.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS logs (id INTEGER, message TEXT)")

# Bulk insert with a single transaction
data = [(i, f"event {i}") for i in range(100000)]
cur.execute("BEGIN TRANSACTION;")
cur.executemany("INSERT INTO logs VALUES (?, ?)", data)
conn.commit()

Batchare i dati come nell’esempio sopra migliora sensibilmente le prestazioni.

DuckDB

DuckDB gestisce in modo efficiente l’ingestione di grandi dataset, soprattutto da file esterni. Usa i comandi COPY o le query dirette su Parquet/CSV per pipeline più veloci.

import duckdb
con = duckdb.connect()
# Bulk load CSV into DuckDB
con.execute(""" CREATE OR REPLACE TABLE logs AS SELECT * FROM read_csv_auto('transactions.csv') """)

2. Tecniche di ottimizzazione delle query

SQLite: 

Per ottimizzare le query, usa indici per i lookup.

CREATE INDEX idx_customer_id ON orders(customer_id);

Consiglio anche di usare EXPLAIN QUERY PLAN per analizzare i piani di esecuzione e identificare indici mancanti.

In generale, mantieni le query semplici ed evita join non necessari in ambienti con risorse limitate.

DuckDB

Per DuckDB, puoi sfruttare column pruning e predicate pushdown.

  • Column pruning: DuckDB legge solo le colonne realmente necessarie alla query. Ciò riduce l’I/O e accelera gli scan, soprattutto con tabelle larghe o file Parquet.
  • Predicate pushdown: DuckDB spinge i filtri WHERE nello scan di tabella/file per saltare row group/pagine/file che non possono corrispondere. Questo porta a leggere molti meno byte usando statistiche di file/partizione (ad es., min/max di Parquet).

Esempio di column pruning:

-- Only reads the 'user_id' and 'amount' columns; other columns aren’t touched
SELECT user_id, SUM(amount)
FROM read_parquet('events/*.parquet')
GROUP BY user_id;

Esempio di predicate pushdown (Parquet):

-- Skips row groups/pages whose min/max(order_ts) are entirely outside this range
SELECT *
FROM read_parquet('events/dt=*/events.parquet')
WHERE order_ts >= TIMESTAMP '2025-09-01'
  AND order_ts <  TIMESTAMP '2025-10-01';

3. Manutenzione e considerazioni operative

  • SQLite: esegui regolarmente VACUUM per recuperare spazio. Per applicazioni embedded, assicurati di avere I/O su disco sufficiente e usa la modalità WAL per migliore durabilità e accesso concorrente.
  • DuckDB: ottimizzato per analitiche; tuning minimo richiesto. Organizza grandi dataset in file Parquet partizionati per sfruttare il partition pruning.Esegui regolarmente benchmark delle query su workload di esempio per regolare le impostazioni di parallelismo (tramite PRAGMA threads).

Sviluppo, integrazione ed ecosistema

I database devono integrarsi bene per garantire un accesso semplice ai dati. Vediamo ora come ciascun database si integra con altri sistemi.

1. Supporto ai linguaggi di programmazione e API

  • SQLite: ampio supporto linguaggi. Offre uno dei ventagli più ampi di binding linguistici nel mondo dei database. Driver ufficiali e mantenuti dalla community esistono per Python, Java, C#, C/C++, PHP, Go, Ruby, Rust e più di altri 30.
  • DuckDB: forti integrazioni per la data science. Si concentra su binding orientati alla data science: supporto ufficiale per Python, R e C/C++.

2. Integrazione con data science e analytics

DuckDB: funziona direttamente con pandas DataFrame.

Esempio:

import duckdb 
import pandas as pd

df = pd.DataFrame({"x": [1,2,3], "y": [10,20,30]})
duckdb.query("SELECT AVG(y) FROM df").show()

DuckDB supporta anche funzioni definite dall’utente (UDF) in SQL e Python, consentendo analitiche avanzate e trasformazioni personalizzate.

Esempio (UDF Python in DuckDB):

import duckdb
import math

con = duckdb.connect()

# Register a Python function as UDF
con.create_function("sqrt_plus", lambda x: math.sqrt(x) + 1)

result = con.execute("SELECT sqrt_plus(16)").fetchall()
print(result)  # [(5.0,)]

3. Integrazione con cloud e infrastruttura

SQLite

SQLite è un database local-first progettato principalmente per lo storage locale embedded.

È usato in applicazioni cloud-native come livello di persistenza leggero o cache, spesso nei container. Pur non avendo connettori di storage cloud diretti, i database SQLite sono portabili e facilmente distribuibili tra ambienti.

DuckDB

DuckDB sta vedendo una crescente adozione nel cloud (query su file Parquet in S3) grazie all’ottimizzazione per architetture dati moderne.

Offre anche supporto nativo per interrogare formati di storage cloud come file Parquet e Arrow su AWS S3, Azure Blob Storage o Google Cloud Storage.

DuckDB si adatta bene a scenari di analytics serverless, dove i dati sono conservati in object storage e interrogati on-demand senza bisogno di un data warehouse pesante.

Limitazioni e trade-off

Nonostante i loro punti di forza, sia DuckDB che SQLite presentano vincoli intrinseci legati alle rispettive filosofie progettuali. 

  • Vincoli di concorrenza e scalabilità:
    • SQLite: concorrenza in scrittura limitata.
    • DuckDB: modello di concorrenza a singolo processo.
  • Vincoli di memoria e gestione risorse:
    • SQLite: leggero, ma non scala a dataset molto grandi.
    • DuckDB: può fare spill su disco, ma non è progettato per durabilità transazionale su larga scala.
  • Lacune di funzionalità:
    • SQLite: funzionalità analitiche limitate.
    • DuckDB: robustezza transazionale limitata.
  • Trade-off e limitazioni prestazionali:
    • SQLite: veloce per transazioni, lento per analitiche.
    • DuckDB: veloce per analitiche, più lento per piccole scritture.

SQLite vs DuckDB: considerazioni finali

DuckDB e SQLite hanno entrambi ruoli preziosi ma distinti:

  • Scegli SQLite se ti serve un database transazionale semplice e leggero per app e sistemi embedded.
  • Scegli DuckDB se ti servono analitiche ad alte prestazioni direttamente nei tuoi workflow Python/R.

In molti scenari, si completano a vicenda: SQLite per storage e transazioni, DuckDB per query analitiche su quei dati. La scelta giusta dipende dal fatto che il tuo workload sia più OLTP (transazioni) o OLAP (analitiche).

Vuoi esplorare altri database come DuckDB e SQLite? Dai un’occhiata al nostro tutorial Guida per principianti a SQLite e al nostro Code-Along Introduzione a DuckDB SQL.

DuckDB vs SQLite FAQ

In che modo DuckDB gestisce dataset più grandi della memoria?

DuckDB utilizza un motore di esecuzione vettorializzato e può riversare i risultati intermedi su disco quando le query superano la RAM disponibile. Questo gli consente di elaborare dataset molto più grandi della memoria mantenendo prestazioni ragionevoli, sebbene la velocità dipenda fortemente dall’I/O su disco.

Quali sono le principali differenze prestazionali tra DuckDB e SQLite?

SQLite è ottimizzato per workload transazionali (OLTP), eccellendo in piccoli insert, update e lookup su singole righe. DuckDB, invece, è ottimizzato per workload analitici (OLAP), con aggregazioni, join e scan più veloci su grandi dataset grazie al design colonnare e all’esecuzione parallela.

Si può usare DuckDB per analitiche in tempo reale?

DuckDB è più adatto ad analitiche batch o interattive, non a stream in tempo reale continui. Può analizzare dati freschi rapidamente se ingeriti o accessibili da file, ma manca di funzionalità integrate per streaming e ingestione ad alta frequenza presenti in sistemi real-time dedicati.

In che modo lo storage colonnare di DuckDB migliora le prestazioni delle query?

Lo storage colonnare consente a DuckDB di leggere solo le colonne necessarie a una query, riducendo l’overhead I/O. In combinazione con compressione ed esecuzione vettorializzata, migliora l’efficienza della cache e velocizza operazioni come aggregazioni e filtri su grandi dataset.

Quali sono le limitazioni di DuckDB per workload transazionali ad alta concorrenza?

DuckDB non è progettato per sistemi transazionali multiutente. Supporta transazioni ACID, ma il suo modello di concorrenza è limitato—rendendolo inadatto ad ambienti con molti writer simultanei o update ad alta frequenza. In tali scenari, è preferibile SQLite o un database server-based.


Austin Chia's photo
Author
Austin Chia
LinkedIn

Sono Austin, blogger e autore tech con anni di esperienza come data scientist e data analyst nel settore sanitario. Partito dalla biologia, oggi aiuto altri a fare lo stesso passaggio attraverso il mio blog tecnologico. La mia passione per la tecnologia mi ha portato a collaborare come autore con decine di aziende SaaS, ispirando altre persone e condividendo le mie esperienze.

Argomenti
SQLite
Data Science

I migliori corsi DataCamp

{count, plural, one {Corso} other {Corsi}}

Introduzione ai database in Python

{hours} ore
102K
In questo corso imparerai le basi dei database relazionali e come usarli.
Vedi i dettagliRight Arrow
Inizia Il Corso
Mostra altroRight Arrow
Correlato

{count, plural, one {blog} other {blogs}}

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

{count, plural, one {blog} other {blogs}}

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

{count, plural, one {blog} other {blogs}}

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

Mostra AltroMostra Altro