Sembra che tu sia principalmente interessato alle prestazioni.
Un paio di persone hanno suggerito di dividere in 3 tabelle (tabella delle categorie più una semplice tabella di riferimento incrociato o un modo più sofisticato di modellare la gerarchia degli alberi, come un insieme nidificato o un percorso materializzato), che è la prima cosa che ho pensato quando ho letto la tua domanda .
Con gli indici, un approccio completamente normalizzato come quello (che aggiunge due JOIN) avrà comunque prestazioni di lettura "abbastanza buone". Un problema è che un INSERT o UPDATE per un evento ora può includere anche uno o più INSERT/UPDATE/DELETE nella tabella dei riferimenti incrociati, che su MyISAM significa che la tabella di riferimenti incrociati è bloccata e su InnoDB significa che le righe sono bloccate, quindi se il tuo database è occupato con un numero significativo di scritture, avrai problemi di contesa maggiori rispetto a se solo le righe dell'evento fossero bloccate.
Personalmente, proverei questo approccio completamente normalizzato prima di ottimizzare. Ma presumo che tu sappia cosa stai facendo, che le tue ipotesi siano corrette (le categorie non cambiano mai) e che tu abbia un modello di utilizzo (molte scritture) che richiede una struttura piatta meno normalizzata. Va benissimo e fa parte di ciò di cui tratta NoSQL.
SET contro "molte colonne"
Quindi, per quanto riguarda la tua vera domanda "SET contro molte colonne", posso dire di aver lavorato con due aziende con ingegneri intelligenti (i cui prodotti erano applicazioni Web CRM ... una era in realtà la gestione degli eventi), ed entrambi utilizzato l'approccio "molte colonne" per questo tipo di set di dati statici.
Il mio consiglio è di pensare a tutte le query che farai su questa tabella (ponderate in base alla loro frequenza) e a come funzionerebbero gli indici.
Innanzitutto, con l'approccio "molte colonne" avrai bisogno di indici su ciascuna di queste colonne in modo da poter eseguire SELECT FROM events WHERE CategoryX = TRUE
. Con gli indici, questa è una query super veloce.
Rispetto a SET, per eseguire questa query è necessario utilizzare AND (&), LIKE o FIND_IN_SET() bit per bit. Ciò significa che la query non può utilizzare un indice e deve eseguire una ricerca lineare di tutte le righe (è possibile utilizzare EXPLAIN per verificarlo). Domanda lenta!
Questo è il motivo principale per cui SET è una cattiva idea:il suo indice è utile solo se stai selezionando per gruppi esatti di categorie. SET funziona alla grande se dovessi selezionare le categorie per evento, ma non il contrario.
Il problema principale con l'approccio "molte colonne" meno normalizzato (rispetto alla normalizzazione completa) è che non è scalabile. Se hai 5 categorie e non cambiano mai, va bene, ma se ne hai 500 e le stai cambiando, è un grosso problema. Nel tuo scenario, con circa 30 che non cambiano mai, il problema principale è che c'è un indice su ogni colonna, quindi se esegui scritture frequenti, quelle query diventano più lente a causa del numero di indici che devono essere aggiornati. Se scegli questo approccio, potresti voler controllare il log delle query lente di MySQL per assicurarti che non ci siano query lente anomale a causa di conflitti nelle ore di punta della giornata.
Nel tuo caso, se la tua è una tipica app Web pesante in lettura, penso che l'approccio "molte colonne" (come hanno fatto i due prodotti CRM, per lo stesso motivo) sia probabilmente sensato. È sicuramente più veloce di SET per quella query SELECT.
TL;DR Non utilizzare SET perché la query "seleziona eventi per categoria" sarà lenta.