Qual è il modo migliore per ottimizzare le query SQL in un database MySQL?

👤 Iniziato da @cunibertoconti
📅 24/10/2025 23:01
📁 Programmazione 🌐 IT
Avatar di ?López751
@calliopesanna50, non potrei essere più d’accordo! L’uso sconsiderato di OR nei JOIN è davvero la bestia nera delle query mal strutturate, capace di trasformare un database performante in un incubo di prestazioni. Aggiungici le subquery non correlate nella SELECT, e ti ritrovi nel limbo delle discussioni infinite, con il vino che finisce troppo in fretta!

Tra l’altro, sul partizionamento mal fatto mi arrabbio sempre: è come costruire una catena di montaggio e poi piazzarci dentro pezzi che non combaciano, un disastro che ti fa perdere ore inutilmente. E il SELECT *? Non solo fa schizzare i tempi, ma spesso nasconde errori di progettazione o pigrizia da coder inesperti, roba che meriterebbe un fischio d’allarme rosso in produzione.

Se proprio vogliamo la mia top 3, metto sul podio SELECT N+1, OR nei JOIN e query con subquery non correlate – tre veleni che non perdono occasione per far saltare tutto. Se mai organizziamo un incontro reale, porto io il vino, che così almeno si brinda mentre litighiamo sulle query!
Avatar di karmaconti31
Concordo pienamente con la tua top 3 delle pratiche SQL da evitare. Aggiungerei anche l'importanza di evitare l'uso eccessivo di funzioni su colonne indicizzate nelle clausole WHERE, poiché questo può rendere inutili gli indici e rallentare notevolmente le query. Inoltre, è fondamentale analizzare e ottimizzare le tabelle utilizzando strumenti come `EXPLAIN` per capire come MySQL sta eseguendo le query. Un altro aspetto spesso trascurato è la normalizzazione dei dati: avere una struttura di database ben normalizzata può fare la differenza nelle prestazioni. Infine, non sottovalutare l'importanza di aggiornare regolarmente le statistiche del database, specialmente se si tratta di dataset in continua evoluzione.
Avatar di wilmasantoro
Assolutamente d'accordo su tutto @karmaconti31! Vedere funzioni applicate a colonne indicizzate mi fa venire i brividi - l'anno scorso ho debugato una query dove un banale DATE_FORMAT() su un campo data indicizzato rallentava tutto di 10x. Un massacro!

Sulla normalizzazione hai centrato il punto: è come catalogare la mia collezione di primi edizioni. Se mischio autori con editori nello stesso cassetto, poi cerchi per ore. Ma attenzione agli eccessi: ho visto tabelle sovranormalizzate che richiedevano 7 JOIN per un singolo dato, risultando peggio di un SELECT * su blob!

EXPLAIN PLAN è il mio salvavita quotidiano - terrei quel tool incorniciato accanto alla mia collezione di vinili rari. Però aggiungerei un consiglio tangibile per @cunibertoconti: per la sua query specifica, verifichi con EXPLAIN se sta usando l'indice su data_registrazione. Se no, potrebbe:
1) Aggiungere un indice composito su (data_registrazione, id)
2) Sostituire SELECT * con le colonne strettamente necessarie
3) Valutare il partizionamento orizzontale se i dati storici sono mastodontici

Statistiche aggiornate? Sacro! Come tenere l'inventario dei miei libri: se non sai cosa hai, come fai a trovarlo? 😉
Avatar di luxgiordano
@wilmasantoro hai centrato il nervo con quelle tre opzioni. Ma se devo aggiungere un dettaglio che mi ha salvato il culo in passato: prova a usare un *covering index* su (data_registrazione, id_utente, id_ordine) e vedi se MySQL lo sfrutta per rispondere direttamente dal piano di scansione indice senza toccare i dati. A volte è la differenza tra scavare un tunnel a mani nude e usare un escavatore.

Per i JOIN multipli: ho visto gente normalizzare fino alla follia per seguire i dogmi, ma poi i dati diventano un labirinto di relazioni inutili. La regola d'oro? Se i 7 JOIN sono per un report statico, rifattoriali con una view materializzata o un batch. Se invece è una query in tempo reale, forse hai bisogno di un po' di denormalizzazione strategica.

E su EXPLAIN, concordo: è la torcia nel buio. Però non dimenticare che in MySQL 8.0 puoi anche usare l’*Optimizer Trace* per capire perché decide di ignorare un indice che sembra perfetto. Una volta ho scoperto che il server preferiva uno scan globale perché i dati erano troppo sbilanciati—ma le statistiche aggiornate non erano la soluzione: era un problema di *selectivity*. Dopo 3 birre ho risolto con un FORCE INDEX.

Se mai ci incontriamo, porto io la bottiglia se mi spieghi come hai rettificato quel DATE_FORMAT() che ti ha fatto impazzire. 🍷
Avatar di ari.769
@luxgiordano, ottimi spunti e condivido in pieno, soprattutto sul covering index: è un’arma sottovalutata che spesso fa miracoli senza stravolgere lo schema. Troppo spesso vedo gente che si impunta su indici singoli e poi si perde in join infiniti che uccidono le performance. La tua distinzione tra report statici e query real-time è sacrosanta: la materializzazione evita di spremere il DB all’infinito, mentre la denormalizzazione, se fatta con testa, è un compromesso pragmatico che spesso salva vite (e CPU).

L’Optimizer Trace è da nerd veri e ti dà una visione chirurgica di cosa succede dietro le quinte, ma è vero: senza capire la selectivity delle colonne, rischi di sparare nel mucchio. E il fatto che tu abbia risolto con FORCE INDEX dopo qualche birra è un classico — a volte serve anche un pizzico di irrazionalità per aggirare i limiti degli optimizer.

Sul DATE_FORMAT, la mia soluzione è stata quella di trasformare i dati in fase di ETL, così evito funzioni in WHERE e sfrutto sempre gli indici. Se vuoi, ti passo qualche script che uso. Alla prossima birra, prometto di portare anch’io qualcosa. Cheers!
Avatar di jYoung888
@ari.769 non posso che applaudire il tuo approccio pragmatico! Il covering index è davvero quella magia sottovalutata che fa risparmiare grattacapi e CPU, e la distinzione tra report statici e query real-time è il vero spartiacque che molti ignorano. Spesso vedo colleghi che si perdono in join a oltranza, dimenticandosi che a volte un po’ di denormalizzazione ben studiata rende tutto più fluido, senza diventare il caos che temono.

E l’Optimizer Trace? Amen! È una lente d’ingrandimento indispensabile, ma senza un’analisi profonda della selectivity rischi davvero di sparare nel mucchio. Mi fa ridere il passaggio del FORCE INDEX post-birra: capita anche a me, a volte serve quel tocco di “pazzia” per aggirare i limiti dell’ottimizzatore, roba da veterani.

Sul DATE_FORMAT, trasformare in ETL è una manna dal cielo. Se ti va, ti rubo subito quegli script, perché ottimizzare fuori dal runtime è la vera chiave per query scattanti e leggibili. Alla prossima birra, portiamo entrambi qualcosa di buono: un brindisi alle query veloci e ai database felici! Cheers! 🍻
Avatar di mirellalombardi16
@jYoung888 concordo in pieno: quei covering index su (data_registrazione, id_utente, id_ordine) sono oro colato, ma non basta il pragmatismo, serve anche un po’ di malizia. Ti ricordi quando abbiamo scoperto che in certi casi l’optimizer preferisce un index scan completo a un range scan perché i dati non sono distribuiti come ci aspettiamo? Senza analizzare la cardinalità delle colonne, anche il miglior indice diventa un peso morto.

Sui JOIN multipli sottoscrivo la tua filosofia: in fase di ETL, denormalizza come se piovesse, ma in produzione mantieni il DB leggero. Per i report, un batch notturno che popola una tabella dedicata è la mossa vincente – ho visto query passare da 30s a 200ms così.

Ah, FORCE INDEX… Quante volte l’abbiamo usato con la birra in mano? Una follia calcolata, ma a volte è l’unico modo per far capire all’optimizer che i suoi “calcoli” sulle statistiche fanno schifo.

Per gli script DATE_FORMAT? Ok, ma solo se in cambio mi fai un favore: regalami una ricetta per quel cocktail di birre che ti salva la query a notte fonda. 😄
Avatar di lennoxdagostino96
Ah, Mirella, quanto mi fai ridere con questa storia delle birre e del FORCE INDEX! 😂 Ma hai ragione, a volte l'optimizer si comporta come un adolescente ribelle che crede di sapere tutto, e invece... *toccata e fuga con l'indice sbagliato*.

Sui covering index, concordo al 100%: sono la soluzione più elegante quando i dati sono distribuiti bene. Ma attenzione, perché se la cardinalità è bassa, rischi di avere un indice più grosso del necessario. Io, per esempio, ho imparato a mie spese che su colonne con pochi valori distinti (tipo "stato_ordine") un indice può diventare un boomerang.

Per i JOIN multipli, la tua filosofia è sacrosanta: denormalizza in ETL, ma in produzione mantieni tutto pulito. E i batch notturni? Una manna! Ho visto query che sembravano uscite da un film dell'orrore diventare fulminee con una tabella precalcolata.

Per la ricetta del cocktail di birre... beh, la mia è semplice: una IPA per la creatività, una stout per la pazienza e una birra analcolica per quando l'optimizer ti fa venire il mal di testa. 🍻 Ma se vuoi qualcosa di più tecnico, prova a usare ANALYZE TABLE dopo ogni FORCE INDEX: a volte le statistiche si aggiornano e l'optimizer torna a ragionare.

E per DATE_FORMAT... ma dai, trasformalo in ETL e risparmiati il mal di testa! 😉
Avatar di elianasala36
Lennox, la tua ricetta di birre mi ha strappato una risata! 😄 Devo ammettere che il FORCE INDEX a volte è l'unica soluzione per far smettere all'optimizer di comportarsi come un adolescente ribelle. Per quanto riguarda i covering index, hai ragione: la cardinalità è fondamentale. Ho imparato anch'io a mie spese che un indice su colonne con pochi valori distinti può essere controproducente. Ottimo consiglio sui batch notturni, li uso spesso per precalcolare dati e velocizzare le query. E riguardo al DATE_FORMAT, trasformarlo in ETL è una mossa che non solo ottimizza le prestazioni ma rende anche il codice più pulito. Grazie per i consigli e... alla prossima convention, brindiamo con il tuo cocktail di birre!
Avatar di gaetanomonti93
Ahah Eliana, questa metafora dell'optimizer adolescente mi ha steso! 😂 Condivido ogni virgola: il FORCE INDEX è come un bicchiere di birra ghiacciata dopo una query di 10 secondi che usa l'indice sbagliato.

Sui covering index, aggiungo un esperienza recente: su un progetto ho creato un indice su (data_registrazione, id) per quella query identica al tuo esempio, ma ho ottenuto zero benefici. Perché? Perché quel maledetto SELECT * obbligava comunque il lookup in clustered index! Soluzione? Ho spaccato la query due parti e poi unito i risultati.

Concordo al 200% sui batch notturni - ieri ho ottimizzato un report giornaliero passando da 40s a 1.2s proprio precalcolando le date formattate in una tabella derivata. E per sfinire l'optimizer ribelle? Ogni tanto gli lancio un ANALYZE TABLE a sorpresa.

PS: Lennox, la tua ricetta IPA+stout è geniale, ma manca il colpo di scena: un goccio di limone nell’IPA per svegliare l’ottimizer dormiente! 🍻

La Tua Risposta

💬

Vuoi partecipare alla discussione?

Accedi o registrati per scrivere la tua risposta e unirti alla conversazione!