Parece que te preocupa principalmente el rendimiento.
Un par de personas han sugerido dividir en 3 tablas (tabla de categorías más una tabla de referencia cruzada simple o una forma más sofisticada de modelar la jerarquía del árbol, como un conjunto anidado o una ruta materializada), que es lo primero que pensé cuando leí su pregunta .
Con índices, un enfoque completamente normalizado como ese (que agrega dos JOIN) seguirá teniendo un rendimiento de lectura "bastante bueno". Un problema es que una INSERCIÓN o ACTUALIZACIÓN de un evento ahora también puede incluir una o más INSERCIONES/ACTUALIZACIONES/ELIMINACIONES en la tabla de referencias cruzadas, lo que en MyISAM significa que la tabla de referencias cruzadas está bloqueada y en InnoDB significa que las filas están bloqueadas. por lo tanto, si su base de datos está ocupada con una cantidad significativa de escrituras, tendrá mayores problemas de contención que si solo las filas de eventos estuvieran bloqueadas.
Personalmente, probaría este enfoque completamente normalizado antes de optimizar. Pero supondré que sabe lo que está haciendo, que sus suposiciones son correctas (las categorías nunca cambian) y que tiene un patrón de uso (muchas escrituras) que requiere una estructura plana menos normalizada. Eso está totalmente bien y es parte de lo que trata NoSQL.
SET frente a "muchas columnas"
Entonces, en cuanto a su pregunta real "SET versus muchas columnas", puedo decir que trabajé con dos compañías con ingenieros inteligentes (cuyos productos eran aplicaciones web de CRM... una era en realidad gestión de eventos), y ambas usó el enfoque de "muchas columnas" para este tipo de datos de conjuntos estáticos.
Mi consejo sería pensar en todas las consultas que realizará en esta tabla (ponderadas por su frecuencia) y cómo funcionarían los índices.
Primero, con el enfoque de "muchas columnas", necesitará índices en cada una de estas columnas para poder hacer SELECT FROM events WHERE CategoryX = TRUE
. Con los índices, esa es una consulta súper rápida.
A diferencia de SET, debe usar AND bit a bit (&), LIKE o FIND_IN_SET() para realizar esta consulta. Eso significa que la consulta no puede usar un índice y debe realizar una búsqueda lineal de todas las filas (puede usar EXPLAIN para verificar esto). ¡Consulta lenta!
Esa es la razón principal por la que SET es una mala idea:su índice solo es útil si está seleccionando por grupos exactos de categorías. SET funciona muy bien si selecciona categorías por evento, pero no al revés.
El problema principal con el enfoque de "muchas columnas" menos normalizado (frente al completamente normalizado) es que no se escala. Si tienes 5 categorías y nunca cambian, bien, pero si tienes 500 y las estás cambiando, es un gran problema. En su escenario, con alrededor de 30 que nunca cambian, el problema principal es que hay un índice en cada columna, por lo que si realiza escrituras frecuentes, esas consultas se vuelven más lentas debido a la cantidad de índices que deben actualizarse. Si elige este enfoque, es posible que desee comprobar el registro de consultas lentas de MySQL para asegurarse de que no haya consultas lentas atípicas debido a la contención en las horas punta del día.
En su caso, si la suya es una aplicación web típica de lectura intensiva, creo que optar por el enfoque de "muchas columnas" (como lo hicieron los dos productos de CRM, por la misma razón) probablemente sea sensato. Es definitivamente más rápido que SET para esa consulta SELECT.
TL;RD No utilice SET porque la consulta "seleccionar eventos por categoría" será lenta.