Curso de MySQL

Tipos de datos en MySQL: cuál elegir para cada columna

Por Víctor Peña · Publicado el

Tipos de datos en MySQL

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 DATE no acepta «mañana por la tarde». El tipo es la primera validación.
  • Espacio. Un INT ocupa 4 bytes; un BIGINT, 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:

  • TIMESTAMP se guarda en UTC y se convierte según la zona horaria de la conexión. Ocupa 4 bytes. Su límite es 2038.
  • DATETIME guarda 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 NULL obliga a que tenga valor.
  • NULL permite que esté vacío.
  • DEFAULT define el valor si no se especifica.
  • UNIQUE impide duplicados.
  • AUTO_INCREMENT numera 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

  • FLOAT para 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 NULL por 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.

Solicitar cotizaciónVer todos los servicios

Cotización sin costo · Respuesta directa por WhatsApp