Tema
Tipos de datos
Cada columna de una tabla tiene un tipo de dato que define qué puede guardar: números, texto, fechas... Elegir bien el tipo evita errores (como precios que pierden centavos), ahorra espacio y hace tus consultas más rápidas. En esta lección verás los tipos más usados y sus diferencias entre MySQL, PostgreSQL y SQLite.
Concepto
El tipo de dato es como el tamaño y forma de un cajón: en un cajón para cucharas no caben platos. Si declaras una columna como número entero, la BD rechazará (o convertirá) un texto como 'hola'.
Elegir el tipo correcto te da:
- Validación automática: no se cuelan datos absurdos.
- Operaciones correctas: sumar números, restar fechas, ordenar alfabéticamente.
- Rendimiento: un
INTocupa 4 bytes; el mismo número guardado como texto ocupa más y se compara más lento.
Tipos numéricos
Enteros
| Tipo | Bytes | Rango aproximado | Uso típico |
|---|---|---|---|
SMALLINT | 2 | -32 mil a 32 mil | Edades, cantidades pequeñas |
INT / INTEGER | 4 | -2,100 millones a 2,100 millones | IDs, stock, cantidades |
BIGINT | 8 | ±9 trillones | IDs de tablas enormes, contadores |
MySQL además tiene TINYINT (1 byte) y el modificador UNSIGNED (solo positivos, doble de rango).
Decimales: el tema del dinero
| Tipo | Exacto | Uso |
|---|---|---|
DECIMAL(p, s) / NUMERIC(p, s) | Sí | Dinero, cantidades exactas |
FLOAT / REAL / DOUBLE | No (aproximado) | Medidas científicas, coordenadas |
DECIMAL(10,2) significa: 10 dígitos en total, 2 de ellos después del punto. Máximo: 99999999.99.
sql
-- Prueba esto en PostgreSQL o MySQL:
SELECT 0.1 + 0.2; -- con DECIMAL: 0.3
SELECT CAST(0.1 AS DOUBLE PRECISION) + CAST(0.2 AS DOUBLE PRECISION);
-- resultado: 0.30000000000000004 ← error de redondeo binarioNunca uses FLOAT para dinero
Los tipos de punto flotante guardan aproximaciones binarias. Tras miles de operaciones aparecen centavos fantasma en los reportes. Para precios, saldos y totales usa siempre DECIMAL(10,2) (o NUMERIC en PostgreSQL).
Tipos de texto
| Tipo | Descripción | Ejemplo de uso |
|---|---|---|
CHAR(n) | Longitud fija; rellena con espacios | Códigos de país 'MX', CURP (18) |
VARCHAR(n) | Longitud variable hasta n caracteres | Nombres, emails, títulos |
TEXT | Texto largo sin límite práctico | Descripciones, artículos de blog |
sql
CREATE TABLE ejemplo_texto (
pais CHAR(2), -- siempre 2 letras: 'MX', 'AR'
nombre VARCHAR(100), -- hasta 100 caracteres
biografia TEXT -- texto largo
);Buena práctica
Ponle a VARCHAR un límite razonable: VARCHAR(100) para nombres, VARCHAR(150)–VARCHAR(255) para emails. Sirve como validación extra. En MySQL usa el charset utf8mb4 para soportar acentos y emojis (lo verás en utf8mb4 y collation).
Fechas y horas
| Tipo | Guarda | Ejemplo |
|---|---|---|
DATE | Solo fecha | '2024-05-02' |
TIME | Solo hora | '14:30:00' |
DATETIME (MySQL) / TIMESTAMP | Fecha y hora | '2024-05-02 14:30:00' |
TIMESTAMPTZ (PostgreSQL) | Fecha, hora y zona horaria | '2024-05-02 14:30:00-06' |
El formato estándar es ISO 8601: AAAA-MM-DD. Úsalo siempre al escribir fechas; '02/05/2024' es ambiguo (¿2 de mayo o 5 de febrero?).
sql
-- Fecha y hora actuales
SELECT CURRENT_DATE; -- 2024-06-20
SELECT CURRENT_TIMESTAMP; -- 2024-06-20 10:15:42Buena práctica
Para fechas de eventos (cuándo se creó un pedido) guarda la hora en UTC o usa un tipo con zona horaria (TIMESTAMPTZ en PostgreSQL). Convertir a la hora local del usuario es trabajo de la aplicación.
Booleanos
| SGBD | Tipo | Valores |
|---|---|---|
| PostgreSQL | BOOLEAN | TRUE, FALSE, NULL |
| MySQL | BOOLEAN (alias de TINYINT(1)) | 1, 0 (acepta TRUE/FALSE) |
| SQLite | No tiene; usa INTEGER | 1, 0 |
sql
CREATE TABLE usuarios (
id INT PRIMARY KEY,
activo BOOLEAN NOT NULL DEFAULT TRUE
);Otros tipos útiles
| Tipo | Disponible en | Para qué |
|---|---|---|
ENUM('a','b') | MySQL (PostgreSQL con CREATE TYPE) | Lista cerrada de valores |
JSON / JSONB | MySQL / PostgreSQL | Datos semiestructurados |
UUID | PostgreSQL (MySQL: CHAR(36) o BINARY(16)) | Identificadores únicos globales |
BLOB / BYTEA | MySQL / PostgreSQL | Datos binarios (mejor guarda archivos fuera de la BD) |
NULL: la ausencia de valor
NULL significa "desconocido" o "no aplica". No es cero, ni texto vacío, ni falso.
| Valor | Significado |
|---|---|
0 | Sabemos que es cero |
'' | Sabemos que es un texto vacío |
NULL | No sabemos / no existe |
Reglas importantes:
- Cualquier operación con
NULLdaNULL:5 + NULLesNULL. NULL = NULLno es verdadero (esNULL). Para preguntar se usaIS NULLoIS NOT NULL.- Usa
NOT NULLen las columnas obligatorias.
sql
-- MAL: nunca devuelve filas
SELECT * FROM productos WHERE categoria_id = NULL;
-- BIEN
SELECT * FROM productos WHERE categoria_id IS NULL;Resultado (con nuestros datos):
| id | nombre | categoria_id | precio | stock |
|---|---|---|---|---|
| 8 | Cable USB-C | NULL | 99.00 | 100 |
Para reemplazar un NULL por un valor por defecto en una consulta usa COALESCE:
sql
SELECT nombre, COALESCE(ciudad, 'Sin ciudad') AS ciudad FROM clientes;Tabla comparativa por SGBD
| Necesidad | MySQL / MariaDB | PostgreSQL | SQLite |
|---|---|---|---|
| Entero | INT | INTEGER | INTEGER |
| ID autoincremental | INT AUTO_INCREMENT | INT GENERATED ALWAYS AS IDENTITY | INTEGER PRIMARY KEY |
| Dinero | DECIMAL(10,2) | NUMERIC(10,2) | NUMERIC (guarda como real) |
| Texto corto | VARCHAR(n) | VARCHAR(n) o TEXT | TEXT |
| Texto largo | TEXT, LONGTEXT | TEXT | TEXT |
| Fecha | DATE | DATE | TEXT en ISO 8601 |
| Fecha y hora | DATETIME, TIMESTAMP | TIMESTAMP, TIMESTAMPTZ | TEXT |
| Booleano | BOOLEAN (=TINYINT(1)) | BOOLEAN | INTEGER 0/1 |
| JSON | JSON | JSON, JSONB | TEXT + funciones JSON |
SQLite es flexible
SQLite usa "afinidad de tipos": acepta casi cualquier valor en cualquier columna. Es cómodo para aprender, pero en MySQL y PostgreSQL los tipos se respetan con más rigor.
Ejemplo: la tabla productos con tipos bien elegidos
sql
CREATE TABLE productos (
id INT PRIMARY KEY, -- entero: identificador
nombre VARCHAR(100) NOT NULL, -- texto corto obligatorio
categoria_id INT NULL, -- puede no tener categoría
precio DECIMAL(10,2) NOT NULL, -- dinero: exacto
stock INT NOT NULL DEFAULT 0 -- entero, 0 si no se indica
);Paso a paso: cómo elegir un tipo
- ¿Es un número con el que harás cuentas? →
INToDECIMAL. ¿Es dinero? →DECIMAL. - ¿Es un "número" que en realidad es un código (teléfono, código postal, CURP)? → texto (
VARCHAR). El código postal01000perdería su cero inicial como número. - ¿Es una fecha? →
DATE/TIMESTAMP, nunca texto. - ¿Es sí/no? →
BOOLEAN. - ¿Es obligatorio? →
NOT NULL. ¿Tiene un valor típico? →DEFAULT.
Error común
Guardar teléfonos como INT: se pierden ceros iniciales, no caben los de 10+ dígitos en algunos tipos y no puedes guardar +52. Un teléfono es texto: VARCHAR(20).
Ejercicios
- Elige el tipo para: a) precio de un curso, b) código postal, c) fecha de nacimiento, d) ¿el usuario aceptó términos?, e) descripción larga de un producto, f) número de piso de un edificio.
- ¿Qué devuelve
SELECT 10 + NULL;? ¿Por qué? - Escribe la definición de la tabla
clientesdel curso con tipos adecuados (id, nombre, email, ciudad opcional, fecha_registro). - ¿Por qué esta consulta no devuelve nada aunque haya clientes sin ciudad?
SELECT * FROM clientes WHERE ciudad = NULL;
Ver solución
- a)
DECIMAL(10,2), b)VARCHAR(10)(oCHAR(5)en México), c)DATE, d)BOOLEAN, e)TEXT, f)SMALLINT. NULL. Cualquier operación aritmética con un valor desconocido produce un resultado desconocido.
sql
CREATE TABLE clientes (
id INT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
ciudad VARCHAR(80),
fecha_registro DATE NOT NULL
);- Porque comparar con
NULLusando=nunca es verdadero. Debe serWHERE ciudad IS NULL.
Resumen
- Cada columna tiene un tipo: elegirlo bien valida datos y mejora el rendimiento.
- Enteros:
INT/BIGINT. Dinero: siempreDECIMAL(10,2). - Texto:
VARCHAR(n)para datos cortos,TEXTpara largos. Teléfonos y códigos postales son texto. - Fechas en formato ISO
AAAA-MM-DDcon tiposDATEoTIMESTAMP. NULLes "desconocido": compáralo conIS NULLy reemplázalo conCOALESCE.