L'SQL non è difficile da scrivere; è difficile scriverlo bene.
Beekeeper Studio è una GUI per database gratuita & open source
Il miglior strumento per query & editor SQL che abbia mai usato. Offre tutto ciò che mi serve per gestire il mio database. - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studio è veloce, intuitivo e facile da usare. Beekeeper supporta tantissimi database e funziona alla grande su Windows, Mac e Linux.
Le query che funzionano bene in sviluppo iniziano a rallentare in produzione. Un ORDER BY mancante restituisce risultati incoerenti, un SELECT * veloce si rompe quando lo schema cambia, e filtri che sembrano innocui su un piccolo dataset possono bloccarsi quando i dati crescono.
Col tempo inizi a riconoscere gli schemi che reggono e quelli che non reggono.
Ecco quindi sette best practice SQL, offerte dal team di Beekeeper Studio, la GUI per database moderna, open source e multipiattaforma.
🔖 Riferimento rapido: mentre impari queste best practice, tieni a portata di mano il nostro cheat sheet del linguaggio SQL per consultare al volo SELECT, WHERE, JOIN, aggregazioni e altri comandi SQL essenziali.
1. Sii specifico: evita SELECT *
Recuperare tutte le colonne con SELECT * può sembrare rapido e flessibile. Non devi decidere quali colonne restituire e quali escludere, e funziona bene quando stai ancora esplorando un dataset.
Questa flessibilità ha un prezzo. Man mano che lo schema evolve o i join si moltiplicano, SELECT * può portarsi dietro colonne duplicate o inattese, rallentare le query e rendere il debug più difficile.
Scegliere colonne specifiche rende chiaro l'intento e protegge il tuo codice da rotture silenziose.
-- Risky
SELECT * FROM user_profiles WHERE user_id = 42;
-- Better
SELECT user_id, display_name, avatar_url
FROM user_profiles
WHERE user_id = 42;
2. Usa le CTE per semplificare la logica complessa
Le subquery annidate funzionano, ma diventano presto illeggibili. È difficile capire cosa viene filtrato e dove, e il debug di solito significa scorrere avanti e indietro tra strati di parentesi.
Le Common Table Expression (clausole WITH) ti permettono di suddividere una query in passaggi con un nome. Ogni passaggio si può eseguire e testare da solo, così l'intento è più chiaro e gli errori più facili da individuare. Ricorda però che alcuni database materializzano le CTE, quindi nei cicli stretti le subquery inline possono essere più veloci.
-- Hard to follow
SELECT customer_id, COUNT(*)
FROM (
SELECT customer_id, created_at
FROM orders
WHERE created_at > now() - interval '7 days'
) o
GROUP BY customer_id;
-- Clearer
WITH recent_orders AS (
SELECT customer_id, created_at
FROM orders
WHERE created_at > now() - interval '7 days'
)
SELECT customer_id, COUNT(*)
FROM recent_orders
GROUP BY customer_id;
3. Commenta l'intento, non la sintassi
I commenti inline che ripetono ciò che l'SQL mostra già non aggiungono molto. Il contesto che dimenticherai davvero non è cosa fa la query, ma perché esiste.
Una breve nota sullo scopo o sull'utilizzo rende più facile riprendere in mano il codice o passarlo ad altri. Scrivi per il te del futuro o per il prossimo sviluppatore che cercherà di capire perché quella query è importante.
-- Find users who signed up but never activated
-- Used in the weekly retention cohort report
SELECT u.id, u.email
FROM users u
LEFT JOIN events e
ON u.id = e.user_id
AND e.name = 'account_activated'
WHERE e.id IS NULL
AND u.created_at > now() - interval '7 days';
4. Scrivi filtri che sfruttano gli indici
Gli indici aiutano solo se i filtri sono sargable (abbreviazione di Search ARGument ABLE). In pratica, significa che il database può usare direttamente l'indice invece di scansionare ogni riga.
Se avvolgi una colonna in una funzione, ad esempio LOWER(email) o DATE(created_at), l'optimizer non può usare l'indice. Anche le conversioni di tipo possono avere lo stesso effetto. La query funziona comunque, ma diventa più lenta man mano che i dati crescono.
-- Not sargable: disables index
WHERE LOWER(email) = 'test@example.com';
-- Sargable: uses existing index
WHERE email = 'test@example.com';
Per mantenere i filtri efficienti, scrivili in modo coerente con come sono memorizzati i dati. Se ti serve un confronto case-insensitive, salva i valori in un formato uniforme o aggiungi un indice funzionale. Per le date, filtra per intervallo invece di trasformare la colonna: è più chiaro e l'indice resta utilizzabile.
-- Not sargable
WHERE DATE(created_at) = '2025-08-04';
-- Sargable
WHERE created_at >= '2025-08-04'
AND created_at < '2025-08-05';
5. Filtra prima di fare la join
Viene la tentazione di unire tutto prima e filtrare dopo, soprattutto quando esplori o fai prototipi. Ma ogni join moltiplica le righe, e ogni riga superflua rende la query più lenta e più difficile da pianificare per l'optimizer.
Restringi invece ogni tabella prima di combinarle. Così il database unisce solo i dati che contano davvero, riducendo l'uso di memoria, migliorando l'efficienza della cache e mantenendo prevedibile il piano di esecuzione.
-- Joins every user, even inactive ones
SELECT e.*
FROM users u
JOIN events e ON e.user_id = u.id;
-- Filter first: only active, recently seen users
WITH active_users AS (
SELECT id
FROM users
WHERE active = true
AND last_seen > now() - interval '90 days'
)
SELECT e.*
FROM active_users u
JOIN events e ON e.user_id = u.id;
6. Preferisci EXISTS a IN per verificare le relazioni
Quando controlli se esistono righe correlate, IN fa il suo lavoro, finché non smette di farlo. Costruisce una lista completa di valori, li confronta uno per uno e gestisce i NULL in un modo che può nascondere corrispondenze valide.
EXISTS funziona diversamente. Restituisce true appena trova una corrispondenza e non è influenzato dai NULL nella subquery. Il risultato è più veloce e più affidabile, soprattutto su dataset di grandi dimensioni.
-- Works, but can be slower and NULL-prone
SELECT *
FROM users
WHERE id IN (SELECT user_id FROM orders);
-- Clear and efficient: stops at first match
SELECT *
FROM users u
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.user_id = u.id
);
7. Specifica sempre ORDER BY
L'SQL non garantisce l'ordine delle righe, a meno che tu non lo chieda.
Senza un ORDER BY, il database è libero di restituire le righe nella sequenza che gli fa più comodo, e questa può cambiare tra un'esecuzione e l'altra, tra ambienti diversi o anche dopo aggiornamenti minori.
In sviluppo può sembrare stabile, ma appena cambiano indici, piani di esecuzione o volume dei dati, inizierai a vedere paginazioni imprevedibili o risultati delle API incoerenti.
-- Looks fine until it isn't
SELECT id, name
FROM users
WHERE active = true
LIMIT 10;
-- Predictable and reproducible
SELECT id, name
FROM users
WHERE active = true
ORDER BY created_at DESC
LIMIT 10;
Mettiamo tutto insieme
Le query migliori sono quelle che capisci ancora un anno dopo. Hanno un intento chiaro, un comportamento prevedibile e nessuna sorpresa nascosta.
È questo lo spirito dietro queste abitudini. Sono pratiche che usiamo ogni giorno in Beekeeper Studio per mantenere le query leggibili, coerenti e veloci su ogni database con cui lavoriamo.
Puoi scaricare gratis la versione PDF di questa guida da tenere a portata di mano o condividere con il tuo team:
Scarica 7 abitudini SQL per sviluppatori impegnati
Con Beekeeper Studio puoi mettere in pratica queste abitudini ogni giorno, usando un editor SQL moderno, multipiattaforma e open source pensato per farti restare concentrato.
- Oltre 20 database supportati: Postgres, MySQL, SQLite, SQL Server, MongoDB, Redis e altri.
- Editor veloce e reattivo: autocompletamento basato sullo schema, query a schede ed evidenziazione della sintassi.
- Strumenti visuali per i dati: sfoglia tabelle, modifica righe e ispeziona JSON senza perdere il filo.
- Assistente AI (privacy al primo posto): lo strumento di AI coding che mancava per il tuo database: conosce lo schema, funziona con l'LLM che preferisci e ti aiuta a esplorare e perfezionare le query più in fretta.