Impara / Cheat sheet
Cheat sheet SQL per tester
L’SQL che i tester usano per verificare i dati dietro una UI o un’API: trovare il record, controllare che sia cambiato e cercare duplicati, record orfani e NULL che non dovrebbero esserci. La sintassi è SQL standard e funziona su PostgreSQL, MySQL 8 e SQLite. Dove un database si comporta diversamente, la nota lo segnala.
Verificato su SQL standard; verificato su PostgreSQL 16, MySQL 8 e SQLite 3
Trovare righe
| Attività | Codice |
|---|---|
| Colonne di una tabella | SELECT id, email FROM users; |
| Filtro | SELECT * FROM orders WHERE status = 'paid' AND total > 100; |
| Uno tra più valori | WHERE status IN ('paid', 'shipped') |
| Intervallo (estremi inclusi) | WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31' |
| Pattern | WHERE email LIKE '%@example.com' |
| Valore mancante | WHERE phone IS NULL |
| Ordina, dal più recente | ORDER BY created_at DESC |
| Prime 10 righe | LIMIT 10 |
| Valori distinti | SELECT DISTINCT status FROM orders; |
SQL Server usa SELECT TOP 10 al posto di LIMIT. BETWEEN sui timestamp include solo la mezzanotte della data finale: per giornate intere usa < giorno successivo.
Aggregare
| Attività | Codice |
|---|---|
| Conta le righe | SELECT COUNT(*) FROM orders; |
| Conta i valori non NULL | SELECT COUNT(shipped_at) FROM orders; |
| Conta i valori distinti | SELECT COUNT(DISTINCT user_id) FROM orders; |
| Totali per gruppo | SELECT status, COUNT(*), SUM(total) FROM orders GROUP BY status; |
| Filtra i gruppi | SELECT user_id, COUNT(*) FROM orders GROUP BY user_id HAVING COUNT(*) > 5; |
| Minimo, massimo, media | SELECT MIN(total), MAX(total), AVG(total) FROM orders; |
WHERE filtra le righe prima del raggruppamento; HAVING filtra i gruppi dopo.
Join
| Attività | Codice |
|---|---|
| Solo le righe corrispondenti | SELECT o.id, u.email FROM orders o JOIN users u ON u.id = o.user_id; |
| Mantieni tutte le righe di sinistra | SELECT u.email, o.id FROM users u LEFT JOIN orders o ON o.user_id = u.id; |
| Righe senza corrispondenza (anti-join) | SELECT u.* FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE o.id IS NULL; |
| Lo stesso, con NOT EXISTS | SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id); |
Un join su una chiave uno-a-molti ripete il lato "uno". Se SUM() sembra troppo grande dopo un join, il join ha moltiplicato le righe.
Controlli sui dati che fanno i tester
| Attività | Codice |
|---|---|
| Duplicati | SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1; |
| Orfani (figlio senza genitore) | SELECT o.* FROM orders o LEFT JOIN users u ON u.id = o.user_id WHERE u.id IS NULL; |
| Campo obbligatorio vuoto | SELECT * FROM users WHERE email IS NULL OR TRIM(email) = ''; |
| Valori fuori intervallo | SELECT * FROM order_items WHERE quantity <= 0 OR unit_price < 0; |
| Totale testata ≠ righe d’ordine | SELECT o.id FROM orders o JOIN order_items i ON i.order_id = o.id GROUP BY o.id, o.total HAVING o.total <> SUM(i.quantity * i.unit_price); |
| La riga esiste dopo un’azione nella UI | SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 1; |
Trappole del NULL
| Trappola | Cosa succede |
|---|---|
| WHERE col = NULL | Mai vero. Usa IS NULL / IS NOT NULL. |
| WHERE status <> 'paid' | Esclude le righe in cui status è NULL. Aggiungi OR status IS NULL se le vuoi includere. |
| NOT IN (subquery che restituisce un NULL) | Non restituisce alcuna riga. Usa invece NOT EXISTS. |
| COUNT(col) vs COUNT(*) | COUNT(col) salta i NULL, COUNT(*) conta ogni riga. |
| Aritmetica con NULL | price + NULL è NULL. Usa COALESCE(col, 0) se 0 è il default corretto. |
Window function
| Attività | Codice |
|---|---|
| Numera le righe per gruppo | ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) |
| Riga più recente per gruppo | SELECT * FROM (SELECT o.*, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn FROM orders o) t WHERE rn = 1; |
| Classifica con pari merito | RANK() OVER (ORDER BY total DESC) / DENSE_RANK() … |
| Valore della riga precedente | LAG(status) OVER (PARTITION BY test_name ORDER BY run_at) |
| Totale progressivo | SUM(total) OVER (ORDER BY created_at) |
Supportate in PostgreSQL, MySQL 8+, SQLite 3.25+ e SQL Server. Una window function non si può usare nella WHERE: filtra in una query esterna.
Modificare i dati in sicurezza
| Attività | Codice |
|---|---|
| Inserimento | INSERT INTO users (email, name) VALUES ('qa+1@example.com', 'QA One'); |
| Aggiornamento (sempre con WHERE) | UPDATE orders SET status = 'cancelled' WHERE id = 1043; |
| Cancellazione (sempre con WHERE) | DELETE FROM sessions WHERE user_id = 42; |
| Prova e poi annulla | BEGIN; UPDATE orders SET status = 'cancelled' WHERE id = 1043; SELECT status FROM orders WHERE id = 1043; ROLLBACK; |
Esegui prima la stessa WHERE come SELECT e controlla il numero di righe prima di fare UPDATE o DELETE.