# Query lente: misurarle prima di aggiungere un indice

Una query lenta si misura con EXPLAIN QUERY PLAN e cinque esecuzioni. Su 200.000 righe SQLite la lettura completa costa 13,2 ms, l'indice 0,29 ms.

> Pagina: https://ui.eneawork.it/blog/performance-database-query
> Markdown: https://ui.eneawork.it/blog/performance-database-query.md

*2026-11-01*

Una query lenta si misura prima di toccarla: EXPLAIN QUERY PLAN dice se il database legge tutta la tabella, e cinque esecuzioni ripetute danno un tempo mediano su cui ragionare. Su 200.000 righe SQLite la stessa condizione passa da 13,2 ms a 0,29 ms quando l'indice giusto esiste.

Tabella SQLite da 200.000 righe creata in memoria con node e il modulo sqlite3, su Windows, il 1 novembre 2026: cinque esecuzioni per query, mediana. Lettura completa su library = 'lib-1000': 13,2 ms. Stessa query dopo CREATE INDEX: 0,29 ms, cioè 45 volte meno. La ricerca tags LIKE '%chart%' senza indice: 52,49 ms su 28.572 righe. Il caricamento delle 200.000 righe ha richiesto 2,8 s. Il catalogo EneUI misura 111.985 componenti, 1.556 blocchi e 12.808 icone da 1.472 librerie, letti da /api/stats il 1 novembre 2026. Comandi e soglie: documentazione SQLite, pubblico dominio.

## Tre numeri prima di un indice

## Il comando

sqlite3 catalogue.db <<'SQL' .timer on EXPLAIN QUERY PLAN SELECT id FROM components WHERE library = 'lib-1000'; SELECT count(*) FROM components WHERE library = 'lib-1000'; CREATE INDEX idx_library ON components (library); SELECT count(*) FROM components WHERE library = 'lib-1000'; SQL

Il comando richiede il client sqlite3 e un file di prova con la tabella components. .timer on stampa la durata di ogni istruzione: la prima SELECT legge tutta la tabella, la seconda usa l'indice creato nel frattempo. La nostra misura ha usato lo stesso schema con node e sqlite3, perché serviva la mediana di più esecuzioni, non una sola.

## Quando l'indice non aiuta

Un indice non è una bacchetta: risolve l'uguaglianza e gli intervalli su una colonna, non un LIKE '%chart%'. La wildcard iniziale obbliga il motore a leggere ogni riga, ed è il motivo dei 52,49 ms misurati. Non aiuta nemmeno su una colonna con due valori distinti: metà tabella resta metà tabella. In quei casi si cambia il modello, si aggiunge una tabella di ricerca o si accetta il costo.

## Il limite da dichiarare

La tabella era in memoria, senza I/O su disco e senza concorrenza: i millisecondi assoluti sono più bassi di quelli di un server vero. Il rapporto tra lettura completa e indice è la parte da portare via, non il valore assoluto. Un database in produzione aggiunge cache, lock e query concorrenti, e un indice in più rallenta ogni scrittura.

## Dove continuare

Il catalogo EneUI indicizza 111.985 componenti, 1.556 blocchi e il record spotlight card, una scheda che costa una query per ogni richiesta. Per capire quanto pesa il tempo di risposta del server leggi TTFB: cosa misura, per la cache che evita la query Cache-Control spiegato, e per il confronto tra architetture framework frontend a confronto.

## FAQ

### Come capisco se una query usa un indice?

Leggi EXPLAIN QUERY PLAN: SEARCH con USING INDEX significa indice usato, SCAN significa lettura completa della tabella. Il piano non mostra i tempi, solo il percorso.

### Quante esecuzioni servono per un tempo affidabile?

Cinque, con la mediana come risultato. La prima esecuzione paga la cache fredda e il caricamento del motore, quindi è sempre la più lenta e la più fuorviante.

### Perché LIKE '%chart%' resta lento con l'indice?

Perché la wildcard iniziale impedisce di usare l'ordine dell'indice: il motore deve leggere ogni riga e confrontare il testo. Un indice funziona quando il prefisso della stringa è noto.

### Un indice rallenta le scritture?

Sì. Ogni INSERT, UPDATE e DELETE aggiorna anche l'indice, quindi il costo si sposta sulle scritture. Vale la pena quando le letture sono molte di più e il filtro è selettivo.

### Il tempo misurato in locale vale in produzione?

No. Cache, disco, concorrenza e dimensione reale dei dati cambiano il numero. Misura il piano in locale e il tempo sul database di produzione, con gli stessi parametri.
