SET

SET permite almacenar cero o más valores de una lista predefinida en una sola columna. Mientras que ENUM solo admite un valor, SET admite cualquier combinación de los valores definidos. Internamente, MySQL almacena los valores como un mapa de bits, lo que lo hace compacto pero con un límite de 64 valores posibles.

Sintaxis

CREATE TABLE nombre_tabla (
    columna SET('valor1', 'valor2', 'valor3') NOT NULL
);

Ejemplo básico

CREATE TABLE cursos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    dias SET('lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado') NOT NULL,
    modalidad SET('presencial', 'online', 'híbrido') NOT NULL
);
 
INSERT INTO cursos (nombre, dias, modalidad) VALUES
('SQL Básico', 'lunes,miércoles', 'presencial,online'),
('Python Avanzado', 'martes,jueves,sábado', 'presencial'),
('JavaScript', 'lunes,martes,miércoles,jueves,viernes', 'online'),
('Data Science', 'viernes', 'híbrido');
 
SELECT * FROM cursos;
idnombrediasmodalidad
1SQL Básicolunes,miércolespresencial,online
2Python Avanzadomartes,jueves,sábadopresencial
3JavaScriptlunes,martes,miércoles,jueves,viernesonline
4Data Sciencevierneshíbrido

Los valores se pasan como una cadena separada por comas, sin espacios. MySQL almacena la combinación como un entero internamente.

Representación interna

Cada valor del SET corresponde a un bit. El primer valor es el bit 1 (valor 1), el segundo es el bit 2 (valor 2), el tercero es el bit 4, y así sucesivamente:

ValorBitDecimal
lunes11
martes22
miércoles34
jueves48
viernes516
sábado632
SELECT nombre, dias, dias + 0 AS valor_numerico FROM cursos;
nombrediasvalor_numerico
SQL Básicolunes,miércoles5
Python Avanzadomartes,jueves,sábado42
JavaScriptlunes,martes,miércoles,jueves,viernes31
Data Scienceviernes16

'lunes,miércoles' = 1 + 4 = 5. 'martes,jueves,sábado' = 2 + 8 + 32 = 42.

FIND_IN_SET

Para buscar filas que contengan un valor específico, usa FIND_IN_SET:

-- Cursos que incluyen el martes
SELECT nombre, dias FROM cursos WHERE FIND_IN_SET('martes', dias);
nombredias
Python Avanzadomartes,jueves,sábado
JavaScriptlunes,martes,miércoles,jueves,viernes
-- Cursos con modalidad online
SELECT nombre, modalidad FROM cursos WHERE FIND_IN_SET('online', modalidad);
nombremodalidad
SQL Básicopresencial,online
JavaScriptonline

FIND_IN_SET devuelve la posición del valor en el conjunto (1-based), o 0 si no lo encuentra. En un contexto booleano, cualquier valor distinto de 0 es verdadero.

Buscar con LIKE

Puedes usar LIKE, pero es menos preciso que FIND_IN_SET:

-- Podría dar falsos positivos
SELECT nombre FROM cursos WHERE dias LIKE '%lunes%';

Con LIKE, un valor como 'martelunes' coincidiría (aunque en este caso no existe). FIND_IN_SET es más seguro porque busca valores completos separados por comas.

Buscar con operaciones de bits

Puedes usar operaciones de bits para buscar combinaciones:

-- Cursos que incluyen lunes (bit 1) Y miércoles (bit 3)
SELECT nombre, dias FROM cursos WHERE dias & 5 = 5;
nombredias
SQL Básicolunes,miércoles
JavaScriptlunes,martes,miércoles,jueves,viernes

5 = 1 (lunes) + 4 (miércoles). La condición dias & 5 = 5 verifica que ambos bits estén activos.

Insertar y actualizar

-- Añadir un valor al conjunto existente (sin duplicar)
UPDATE cursos
SET dias = CONCAT(dias, ',sábado')
WHERE id = 1 AND NOT FIND_IN_SET('sábado', dias);
 
-- Reemplazar todo el conjunto
UPDATE cursos SET dias = 'lunes,viernes' WHERE id = 4;

MySQL normaliza automáticamente los valores: elimina duplicados y los ordena según la definición del SET.

Valores inválidos

MySQL rechaza valores que no están en la definición:

INSERT INTO cursos (nombre, dias, modalidad) VALUES ('Test', 'domingo', 'online');
ERROR 1265 (01000): Data truncated for column 'dias' at row 1

Contar valores en un SET

SELECT
    nombre,
    dias,
    (LENGTH(dias) - LENGTH(REPLACE(dias, ',', '')) + 1) AS num_dias
FROM cursos;
nombrediasnum_dias
SQL Básicolunes,miércoles,sábado3
Python Avanzadomartes,jueves,sábado3
JavaScriptlunes,martes,miércoles,jueves,viernes5
Data Sciencelunes,viernes2

La fórmula cuenta las comas y suma 1 para obtener el número de elementos.

SET vs ENUM

CaracterísticaENUMSET
Valores por filaExactamente unoCero o más
Máximo de opciones65,53564
Almacenamiento1-2 bytes1-8 bytes
Uso típicoEstados, categoríasPermisos, tags, días

SET vs tabla de relación

En diseño relacional, almacenar múltiples valores en una columna viola la primera forma normal. La alternativa normalizada es una tabla de relación:

-- Diseño con SET (desnormalizado)
CREATE TABLE cursos_set (
    id INT PRIMARY KEY,
    nombre VARCHAR(100),
    dias SET('lun', 'mar', 'mie', 'jue', 'vie', 'sab')
);
 
-- Diseño normalizado
CREATE TABLE cursos_norm (
    id INT PRIMARY KEY,
    nombre VARCHAR(100)
);
CREATE TABLE curso_dias (
    curso_id INT,
    dia ENUM('lun', 'mar', 'mie', 'jue', 'vie', 'sab'),
    PRIMARY KEY (curso_id, dia),
    FOREIGN KEY (curso_id) REFERENCES cursos_norm(id)
);

El diseño normalizado es más flexible: permite índices eficientes, consultas más claras y no tiene el límite de 64 valores. Usa SET solo cuando la lista de opciones es pequeña, fija y la simplicidad importa más que la flexibilidad.

Limpieza

DROP TABLE IF EXISTS cursos;
DROP TABLE IF EXISTS cursos_set;
DROP TABLE IF EXISTS cursos_norm;
DROP TABLE IF EXISTS curso_dias;

En el siguiente artículo veremos DATE, el tipo para almacenar fechas sin componente de hora.

Escrito por Eduardo Lázaro