CodeGym /Corsi /SQL SELF /Equilibrio tra normalizzazione e performance

Equilibrio tra normalizzazione e performance

SQL SELF
Livello 26 , Lezione 3
Disponibile

Quando normalizziamo i dati alla perfezione, ogni tabella diventa super compatta e ogni informazione segue una sola regola. Però, per fare query reali (tipo "Quali studenti sono iscritti al corso SQL?") spesso serve unire un sacco di tabelle. Più tabelle ci sono, più le query diventano complicate e il sistema "scava con la pala".

Sicuramente ormai conosci già i JOIN dalle lezioni precedenti. Ecco un esempio di query che potresti dover fare con un database progettato in modo normale:

SELECT students.name, courses.title
FROM students
JOIN enrollments ON students.id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.id
WHERE courses.title = 'SQL';

Sembra facile, ma sotto il cofano il server fa un lavoro infernale: legge ogni tabella, unisce i dati, filtra... E se le tabelle sono enormi? Logico pensare che la performance calerà.

La battaglia: normalizzazione vs velocità

Per fortuna (o purtroppo?), nella vita reale i database sono un compromesso. La normalizzazione totale garantisce l'integrità dei dati, ma rallenta le query complesse. Se il database viene usato per analisi e report, a volte conviene denormalizzarlo. È come sostituire 10 scatoline con un baule gigante: recuperi i dati più in fretta, ma rimetterli in ordine è più difficile.

Quando puoi "rilassarti" con la normalizzazione?

Ci sono situazioni dove la denormalizzazione è meglio:

Aggregati usati spesso

Per esempio, immagina che il sistema ogni giorno faccia query per contare quanti studenti ci sono in ogni corso. Con una struttura normalizzata dovresti fare JOIN e COUNT() ogni volta. Invece puoi aggiungere nella tabella "Courses" una colonna student_count, aggiornata in automatico quando aggiungi o togli iscrizioni.

-- Colonna denormalizzata
UPDATE courses
SET student_count = (
    SELECT COUNT(*)
    FROM enrollments
    WHERE enrollments.course_id = courses.id
);

Report frequenti

Se il tuo cliente ogni giorno vuole il report "Chi, dove, quando ha comprato?", è più semplice tenere una tabella denormalizzata con righe già pronte tipo "Nome cliente, prodotto, data". La tabella sarà più grossa, ma recuperi i dati al volo.

Tanti read, pochi write

Quando il database viene usato soprattutto per leggere (tipo per analytics), vale la pena sacrificare la normalizzazione per la velocità.

Minimizzare i join con relazioni complesse

Se tra le tabelle ci sono relazioni multilivello (nested) e i JOIN diventano un incubo, elimina qualche livello di normalizzazione.

Esempio: come la denormalizzazione accelera le cose?

Abbiamo tabelle normalizzate di un e-commerce:

Tabella products Tabella orders Tabella order_items
id id id
name date order_id
price customer_id product_id
quantity

Ogni ordine (orders) è composto da righe ordine (order_items). Calcoliamo quanto ha guadagnato il negozio:

SELECT SUM(order_items.quantity * products.price) AS total_revenue
FROM order_items
JOIN products ON order_items.product_id = products.id;

Il processo di join tra order_items e products rallenta la query su grandi volumi di dati.

Struttura denormalizzata

Ora immaginiamo che nella tabella order_items ci sia una colonna "extra" total_price (denormalizzazione):

Tabella order_items
id
order_id
product_id
quantity
total_price

Ora la query è banale:

SELECT SUM(total_price) AS total_revenue
FROM order_items;

Così evitiamo i JOIN e quindi acceleriamo tutto.

Esercizio pratico: ottimizza il database "Vendite"

Dati: tabelle normalizzate

Tabella products Tabella sales
id id
name product_id
price date
quantity

Obiettivo: velocizzare le query tipo "Quanto abbiamo guadagnato per ogni prodotto?".

Step 1: aggiungi la colonna total_price nella tabella sales:

ALTER TABLE sales ADD COLUMN total_price NUMERIC;

Step 2: riempi questa colonna con i dati già esistenti:

UPDATE sales
SET total_price = quantity * (
    SELECT price
    FROM products
    WHERE products.id = sales.product_id
);

Step 3: ora le query volano:

SELECT product_id, SUM(total_price) AS total_revenue
FROM sales
GROUP BY product_id;

Ma! La denormalizzazione ha un prezzo

Capisci che "più veloce" non vuol dire sempre "meglio". Con la denormalizzazione arrivano dei problemi:

Storage ridondante

La colonna total_price è una copia dei dati, serve spazio extra.

Update complicati

Se il prezzo di un prodotto cambia nella tabella products, dobbiamo aggiornare a mano la colonna total_price corrispondente. Questo può creare inconsistenze.

Anomalie su insert, update e delete

Le informazioni si "desincronizzano" facilmente se ti dimentichi di aggiornare i dati denormalizzati. Per esempio, se cambia il prezzo di un prodotto, non succede in automatico.

Equilibrio: come trovare la via di mezzo?

Scegli cosa conta di più: performance o struttura? Se il database viene letto spesso, adatta la struttura alle query.

Denormalizza solo dove serve. Tipo solo per numeri chiave e report importanti.

Automatizza gli update dei dati denormalizzati. Usa trigger o job per evitare inconsistenze.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION