Important
Diseñado para MySQL 8.0+. Utiliza características específicas como JSON nativo, índices compuestos avanzados y restricciones ENUM que son optimizadas en MySQL.
SET SQL_MODE = 'STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO';Warning
STRICT_TRANS_TABLES: Rechaza datos inválidos en lugar de convertirlos automáticamente
NO_ZERO_DATE: Prohíbe fechas como '0000-00-00'
NO_ZERO_IN_DATE: Prohíbe fechas como '2025-00-15'
ERROR_FOR_DIVISION_BY_ZERO: Error en división por cero en lugar de NULL
¿Por qué lo uso? Para garantizar integridad de datos en producción. Sin esto, MySQL podría aceptar datos corruptos silenciosamente.
PRIMARY KEY (id_usuario),
INDEX idx_email (email),
INDEX idx_usuario_activo (activo),
INDEX idx_rol (rol)| Tipo | Propósito | Características |
|---|---|---|
| PRIMARY KEY | Identificador único de fila | Único, no nulo, automáticamente indexado |
| INDEX | Acelerar búsquedas específicas | Permite duplicados, optimiza consultas WHERE |
Note
¿Por qué estos índices específicos?
idx_email: Para login de usuarios (WHERE email = ?)idx_usuario_activo: Para filtrar usuarios activos (WHERE activo = TRUE)idx_rol: Para consultas por tipo de usuario (WHERE rol = 'admin')
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;| Característica | InnoDB | MyISAM |
|---|---|---|
| Transacciones ACID | ✅ Sí | ❌ No |
| Claves foráneas | ✅ Sí | ❌ No |
| Bloqueo de filas | ✅ Sí | ❌ Solo tablas |
| Recuperación de crasheos | ✅ Automática | ❌ Manual |
Important
InnoDB es esencial para un sistema de encuestas porque necesitamos:
- Consistencia: Si falla al guardar una respuesta, se revierten todos los cambios
- Integridad referencial: No se pueden borrar encuestas con respuestas
- Concurrencia: Múltiples usuarios respondiendo simultáneamente
CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ciWarning
utf8 en MySQL NO es UTF-8 real, solo soporta 3 bytes (sin emojis)
utf8mb4 es UTF-8 completo de 4 bytes (con emojis: 😊, 🚀, 📊)
¿Por qué es crítico? Las encuestas pueden contener emojis y caracteres especiales de cualquier idioma.
CREATE INDEX idx_encuesta_estado_publica ON encuestas(estado, es_publica);Note
Índice compuesto = Un índice que cubre múltiples columnas en orden específico
Ejemplo de consulta optimizada:
-- Esta consulta será SÚPER RÁPIDA
SELECT * FROM encuestas
WHERE estado = 'activa' AND es_publica = TRUE;
-- Sin el índice compuesto, MySQL tendría que:
-- 1. Buscar por estado (rápido)
-- 2. Filtrar resultado por es_publica (lento)
--
-- Con el índice compuesto:
-- 1. Busca directamente la combinación (súper rápido)INDEX idx_sesion_estado_fecha (estado, fecha_inicio) -- En sesiones_respuesta
INDEX idx_respuesta_fecha_pregunta (fecha_respuesta, id_pregunta) -- En respuestasTip
¿Por qué estos índices específicos?
- Reportes por fecha: "Mostrar respuestas del último mes"
- Análisis de abandono: "Sesiones iniciadas vs completadas por periodo"
- Dashboard administrativo: Estadísticas en tiempo real
id_sesion VARCHAR(64) NOT NULL, -- UUID para anonimato
ip_anonimizada VARCHAR(45), -- IP hasheada, no original
user_agent_hash VARCHAR(64), -- Hash del navegadorImportant
¿Por qué VARCHAR(64) para id_sesion?
- Permite UUIDs (36 chars) o hashes SHA-256 (64 chars)
- Es la clave primaria porque NO queremos auto-increment (sería predecible)
- Cada sesión es completamente anónima y única
respuesta_texto LONGTEXT, -- Para respuestas abiertas
respuesta_numerica DECIMAL(15,6), -- Para escalas y números
respuesta_fecha DATE, -- Para fechas sin hora
respuesta_datetime DATETIME, -- Para fechas con hora
respuesta_booleana BOOLEAN, -- Para Sí/NoNote
¿Por qué múltiples columnas para respuestas?
- Rendimiento: Búsquedas numéricas en campo numérico son más rápidas
- Validación: MySQL valida automáticamente tipos de datos
- Análisis: Operaciones matemáticas directas en respuestas numéricas
configuracion JSON, -- En tabla preguntas
metadatos_tecnicos JSON, -- En tabla sesiones_respuestaTip
¿Por qué JSON en lugar de tablas adicionales?
- Flexibilidad: Cada tipo de pregunta tiene configuraciones diferentes
- Performance: Una sola consulta vs múltiples JOINs
- Escalabilidad: Fácil añadir nuevos parámetros sin alterar esquema
Ejemplo de uso:
-- En una pregunta de escala:
{"min_value": 1, "max_value": 10, "labels": {"1": "Muy malo", "10": "Excelente"}}
-- En una pregunta de selección múltiple:
{"allow_other": true, "max_selections": 3, "randomize_options": false}-- Si se borra una encuesta, se borran sus preguntas
FOREIGN KEY (id_encuesta) REFERENCES encuestas(id_encuesta) ON DELETE CASCADE
-- Si se borra un usuario, NO se pueden borrar sus encuestas
FOREIGN KEY (id_creador) REFERENCES usuarios(id_usuario) ON DELETE RESTRICTWarning
CASCADE vs RESTRICT:
- CASCADE: Borra automáticamente registros relacionados
- RESTRICT: Prohíbe borrado si existen registros relacionados
- SET NULL: Pone NULL en lugar de borrar
ALTER DATABASE CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;Important
Esto asegura que TODA la base de datos use UTF8MB4, no solo las tablas. Incluye:
- Nombres de procedimientos almacenados
- Variables de sistema
- Comparaciones de texto
- Ordenamientos (COLLATE)
| Decisión | Alternativa | ¿Por qué elegí esto? |
|---|---|---|
| InnoDB | MyISAM | Transacciones y claves foráneas |
| UTF8MB4 | UTF8 | Soporte completo de emojis |
| JSON | Tablas extra | Flexibilidad y performance |
| VARCHAR(64) para sesiones | INT AUTO_INCREMENT | Anonimato real |
| Índices compuestos | Índices simples | Consultas multi-criterio rápidas |
| ENUM para estados | VARCHAR | Validación automática y menos espacio |
Note
Esta base de datos está optimizada para:
- ✅ Alto volumen de respuestas simultáneas
- ✅ Anonimato completo de respondientes
- ✅ Flexibilidad en tipos de preguntas
- ✅ Análisis rápido de resultados
- ✅ Integridad de datos garantizada