Curso de MySQL
Tipos de datos en MySQL: cuál elegir para cada columna
Por Víctor Peña · Publicado el

Hola, ¿cómo están? Continuando con el curso de MySQL, hoy veremos los tipos de datos. Es la decisión que tomas al crear cada columna, y la que más cuesta cambiar después.
¡Empecemos!
Por qué importa elegir bien
Cada columna de una tabla tiene un tipo que define qué puede guardar. Elegirlo bien tiene tres efectos:
- Integridad. Una columna
DATEno acepta «mañana por la tarde». El tipo es la primera validación. - Espacio. Un
INTocupa 4 bytes; unBIGINT, 8. En una tabla de diez millones de filas, la diferencia importa. - Rendimiento. Cuanto menos ocupa una columna, más filas caben en memoria y más rápidas son las consultas.
Tipos numéricos enteros
| Tipo | Rango con signo | Espacio |
|---|---|---|
TINYINT |
-128 a 127 | 1 byte |
SMALLINT |
-32.768 a 32.767 | 2 bytes |
MEDIUMINT |
±8,3 millones | 3 bytes |
INT |
±2.147 millones | 4 bytes |
BIGINT |
Enorme | 8 bytes |
Si el valor nunca es negativo, añade UNSIGNED y duplicas el rango positivo:
edad TINYINT UNSIGNED, -- 0 a 255
id INT UNSIGNED AUTO_INCREMENT
En la práctica: INT UNSIGNED para identificadores, TINYINT UNSIGNED para edades y cantidades pequeñas, BIGINT solo si esperas superar los dos mil millones de registros.
Sobre el INT(11) que verás en muchos ejemplos: ese número no limita el valor. Solo afecta al relleno visual con ceros, y está en desuso. Un INT(3) guarda perfectamente el número 999999.
Tipos decimales
Aquí está el error más caro de esta lección.
| Tipo | Precisión |
|---|---|
FLOAT |
Aproximada, 4 bytes |
DOUBLE |
Aproximada, 8 bytes |
DECIMAL(p,e) |
Exacta |
Para dinero, siempre DECIMAL. Nunca FLOAT ni DOUBLE.
Es el mismo problema que vimos en el curso de PHP: los números de coma flotante no representan los decimales con exactitud. 0.1 + 0.2 no da exactamente 0.3.
En una columna de precios, ese error se acumula fila por fila hasta que el total del reporte no cuadra con la suma de las facturas, y nadie entiende por qué.
precio DECIMAL(10,2) -- hasta 99.999.999,99
total DECIMAL(12,2)
descuento DECIMAL(5,2) -- hasta 999,99
DECIMAL(10,2) significa 10 dígitos en total, 2 de ellos decimales.
FLOAT y DOUBLE están bien para mediciones científicas, coordenadas o cualquier cosa donde un error mínimo no importe. Para dinero, no.
Tipos de texto
| Tipo | Capacidad | Uso |
|---|---|---|
CHAR(n) |
Fijo, hasta 255 | Longitud siempre igual |
VARCHAR(n) |
Variable, hasta 65.535 | El habitual |
TEXT |
~65.000 caracteres | Textos largos |
MEDIUMTEXT |
~16 millones | Muy largos |
LONGTEXT |
~4.000 millones | Enormes |
CHAR o VARCHAR
CHAR(10) siempre ocupa 10 caracteres, rellenando con espacios. VARCHAR(10) ocupa lo que necesite.
Usa CHAR solo cuando la longitud sea realmente fija: un código de país de dos letras, un hash de longitud conocida. Para todo lo demás, VARCHAR.
Elegir la longitud del VARCHAR
Un criterio práctico:
nombre VARCHAR(100),
apellido VARCHAR(100),
correo VARCHAR(255), -- el estándar de correo permite hasta 254
telefono VARCHAR(20), -- texto, no número
direccion VARCHAR(255),
El teléfono va como texto, no como número. Puede llevar el signo +, espacios o guiones, y los ceros iniciales se perderían si fuera numérico. La regla general: si no vas a hacer cálculos con él, no es un número.
Cuándo usar TEXT
Para descripciones largas, comentarios o contenido de artículos. Ten en cuenta dos cosas: TEXT no admite valor por defecto, y se indexa peor que VARCHAR. Si el texto rara vez supera los 500 caracteres, VARCHAR(500) es mejor opción.
Tipos de fecha y hora
| Tipo | Formato | Rango |
|---|---|---|
DATE |
AAAA-MM-DD |
1000 a 9999 |
TIME |
HH:MM:SS |
±838 horas |
DATETIME |
Fecha y hora | 1000 a 9999 |
TIMESTAMP |
Fecha y hora | 1970 a 2038 |
YEAR |
Año | 1901 a 2155 |
DATETIME o TIMESTAMP
Es la duda clásica, y la diferencia real es esta:
TIMESTAMPse guarda en UTC y se convierte según la zona horaria de la conexión. Ocupa 4 bytes. Su límite es 2038.DATETIMEguarda el valor tal cual, sin conversión. Ocupa 8 bytes y llega hasta el año 9999.
Usa DATETIME para fechas de eventos —una cita médica, una fecha de vencimiento—, donde quieres el valor literal.
Usa TIMESTAMP para marcas de tiempo de registros, aprovechando su comportamiento automático:
creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
actualizado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Esa segunda línea actualiza la columna sola cada vez que se modifica la fila. Es de las cosas más cómodas de MySQL.
Nunca guardes fechas como VARCHAR. Un 15/03/2026 en texto no se puede ordenar ni comparar correctamente, y es ambiguo entre formatos.
Booleanos
MySQL no tiene un tipo booleano real: BOOLEAN es un alias de TINYINT(1).
activo BOOLEAN DEFAULT TRUE,
-- equivale a
activo TINYINT(1) DEFAULT 1
Guarda 0 para falso y 1 para verdadero. Funciona bien, solo conviene saber que por debajo es un entero.
ENUM: valores de una lista cerrada
estado ENUM('pendiente', 'pagado', 'enviado', 'entregado') DEFAULT 'pendiente'
Solo admite los valores de la lista. Es cómodo y ocupa poco.
Pero úsalo con cuidado: agregar un valor nuevo requiere un ALTER TABLE, que en una tabla grande puede tardar. Si la lista va a cambiar con el tiempo, es mejor una tabla aparte con su clave foránea.
Restricciones de columna
Junto al tipo se declaran las reglas:
CREATE TABLE pacientes (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
cedula VARCHAR(20) NOT NULL UNIQUE,
nombre VARCHAR(100) NOT NULL,
telefono VARCHAR(20) NULL,
activo BOOLEAN NOT NULL DEFAULT TRUE,
creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
NOT NULLobliga a que tenga valor.NULLpermite que esté vacío.DEFAULTdefine el valor si no se especifica.UNIQUEimpide duplicados.AUTO_INCREMENTnumera solo.
Marca NOT NULL todo lo que realmente sea obligatorio. Es validación gratuita que el motor aplica siempre, incluso si tu aplicación tiene un fallo.
Y ojo con NULL: no significa «vacío» ni «cero». Significa «desconocido», y se comporta distinto en las comparaciones. Lo veremos al llegar a las consultas.
Una tabla de ejemplo
Con todo lo visto:
CREATE TABLE citas (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
paciente_id INT UNSIGNED NOT NULL,
doctor_id INT UNSIGNED NOT NULL,
fecha DATE NOT NULL,
hora TIME NOT NULL,
diagnostico TEXT NULL,
costo DECIMAL(10,2) NOT NULL DEFAULT 0,
estado ENUM('programada','atendida','cancelada') NOT NULL DEFAULT 'programada',
creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Cada elección tiene su motivo: DATE y TIME separados porque se consultan por separado, DECIMAL para el costo, TEXT para el diagnóstico que puede ser largo, y TIMESTAMP automático para la auditoría.
Errores comunes
FLOATpara dinero. El más caro de todos.- Guardar teléfonos como número y perder los ceros iniciales.
- Fechas en
VARCHAR. VARCHAR(255)para todo, por no pensar la longitud real.- Creer que
INT(11)limita a 11 dígitos. - Todo
NULLpor comodidad, perdiendo la validación del motor.
Para cerrar
Elegir el tipo correcto es la validación más barata que existe: la aplica el motor, siempre, sin que tengas que programar nada.
Si te llevas tres reglas: DECIMAL para dinero, fechas como fecha y nunca como texto, y NOT NULL en todo lo que sea obligatorio.
En la siguiente lección creamos las tablas de nuestro sistema.
Saludos y éxitos.
Norvic Software
Desarrollamos el software que tu empresa necesita
Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.
- Chatbots con Inteligencia Artificial
- Creación de agentes de IA
- Integración de IA en tus sistemas
- Desarrollo de software a medida
- Aplicaciones móviles iOS y Android
- Consultoría y asesoramiento técnico
Cotización sin costo · Respuesta directa por WhatsApp