Tipos de datos
Por qué elegir bien los tipos de datos importa
Elegir tipos de datos no es un detalle menor. Cada tipo tiene implicaciones en:
- Rendimiento (espacio ocupado, velocidad de comparación).
- Integridad (qué valores son válidos o no).
- Portabilidad (si podrás migrar fácilmente a otro motor).
- Semántica (que los datos signifiquen lo que deben significar).
Ejemplo real:
Si guardas fechas como texto ('2025-10-19') en lugar de tipo DATE, no podrás comparar ni ordenar correctamente por fecha.
Tipos numéricos — tamaño, rango y precisión
Enteros
| Tipo | Tamaño | Rango aproximado | Uso típico |
|---|---|---|---|
SMALLINT | 2 bytes | -32.768 a 32.767 | IDs pequeños |
INT o INTEGER | 4 bytes | ±2.000 millones | Claves comunes |
BIGINT | 8 bytes | ±9 cuatrillones | Claves globales o contadores grandes |
SERIAL / BIGSERIAL | auto-incremento | según base | Identificadores automáticos |
Ejemplo DBML:
Table producto {
id_producto int [pk, increment]
stock int [default: 0, note: 'CHECK (stock >= 0)']
}
Si vas a tener menos de 100.000 registros, INT es suficiente. Evita usar BIGINT sin necesidad: ocupa el doble de espacio.
Decimales y reales
| Tipo | Precisión | Características |
|---|---|---|
NUMERIC(p,s) | Precisión exacta, configurable | Ideal para dinero o valores críticos |
REAL / FLOAT | Aproximado (no exacto) | Cálculos científicos, promedios |
Ejemplo (dinero):
Table factura {
id int [pk, increment]
total numeric(10,2) [note: 'CHECK (total >= 0)']
}
NUMERIC(10,2) = hasta 10 dígitos, 2 decimales (99999999.99). Nunca uses FLOAT para dinero o contabilidad: genera redondeos impredecibles.
Tipos de texto — longitud y rendimiento
| Tipo | Características | Uso recomendado |
|---|---|---|
CHAR(n) | Longitud fija | Códigos o abreviaturas (e.g., “ES”, “US”) |
VARCHAR(n) | Longitud variable con límite | Nombres, correos, direcciones |
TEXT | Sin límite fijo | Comentarios, descripciones largas |
Ejemplo:
Table cliente {
id int [pk, increment]
nombre varchar(100) [not null]
correo varchar(150) [unique, not null]
}
VARCHAR es preferible a TEXT en la mayoría de los casos: permite validar longitudes y optimiza índices.
Regla práctica:
- Usa
VARCHAR(n)cuando sepas el tamaño máximo razonable. - Usa
TEXTsolo para campos donde la longitud es impredecible (observaciones, mensajes, etc.).
Tipos de fecha y hora
| Tipo | Descripción | Ejemplo |
|---|---|---|
DATE | Solo fecha (año-mes-día) | '2025-10-19' |
TIME | Solo hora | '14:35:00' |
TIMESTAMP | Fecha + hora | '2025-10-19 14:35:00' |
TIMESTAMPTZ | Fecha + hora con zona horaria | '2025-10-19 14:35:00+02' |
INTERVAL | Duración o diferencia de tiempo | '3 days 4 hours' |
Ejemplo:
Table pedido {
id_pedido int [pk, increment]
fecha timestamp [not null, default: `CURRENT_TIMESTAMP`]
}
CURRENT_TIMESTAMP garantiza que cada registro tenga su marca de creación. Si la aplicación opera en varios países, usa TIMESTAMPTZ.
Tipos booleanos
| Valor lógico | SQL estándar | Alternativas (según motor) |
|---|---|---|
| Verdadero | TRUE o 1 | 't', 'yes' |
| Falso | FALSE o 0 | 'f', 'no' |
Ejemplo:
Table usuario {
id int [pk, increment]
nombre varchar(100)
activo boolean [default: true]
}
BOOLEAN evita errores de interpretación y mejora la legibilidad:
- Mejor que usar
INTcon 0/1. - Más portable que
CHAR(1)con'S'o'N'.
Valores por defecto y consistencia semántica
Los valores por defecto (DEFAULT) ayudan a mantener coherencia en los datos.
Ejemplo:
Table alumno {
id int [pk, increment]
fecha_alta date [default: `CURRENT_DATE`]
pais char(2) [default: 'ES']
activo boolean [default: true]
}
Esto asegura que, si la app no manda esos campos, la base mantenga coherencia y no queden vacíos.
Regla práctica:
- Usa
DEFAULTpara inicializar datos con sentido lógico. - Pero no abuses de ellos para ocultar errores de inserción.
Tipos personalizados
En motores como PostgreSQL, puedes crear dominios para encapsular validaciones repetidas.
Ejemplo:
La forma estándar es declarar el dominio como una tabla ficticia o, mejor aún, como un type personalizado documentado, y luego usarlo en la tabla real acompañado de una nota que preserve la validación.
Primero represento el dominio:
Type telefono_es {
base: varchar(15)
note: 'CHECK (VALUE ~ ''^[0-9]{9}$'')'
}
//
'^[0-9]{9}$': Esta es la expresión regular que impone el formato:
^: Coincide con el comienzo de la cadena.
[0-9]: Coincide con cualquier dígito del 0 al 9.
{9}: Especifica que el dígito anterior ([0-9]) debe aparecer exactamente 9 veces.
$: Coincide con el final de la cadena.//
Y ahora la tabla contacto usando ese tipo:
Table contacto {
id int [pk, increment]
nombre varchar(100)
telefono telefono_es
}
Si el número no cumple el patrón, la base lo rechaza. Puedes usar este dominio en todas las tablas que manejen teléfonos.
Ventajas:
- Reutilizas la regla sin copiarla.
- Facilita el mantenimiento si la validación cambia.
- Mejora la documentación y claridad del modelo.
Fechas y valores por defecto inteligentes
Puedes usar funciones como valores por defecto dinámicos:
Table evento {
id int [pk, increment]
creado_en timestamp [default: `CURRENT_TIMESTAMP`]
actualizado_en timestamp [default: `CURRENT_TIMESTAMP`]
}
Con eso queda perfectamente representado. Luego, mediante un trigger, puedes actualizar actualizado_en automáticamente cuando cambien los datos (veremos esto más adelante en Módulos 26–27 sobre migraciones y mantenimiento).
Compatibilidad y portabilidad
Cada motor tiene matices:
- PostgreSQL y MySQL soportan
NUMERIC(p,s)igual, pero difieren en precisión. TEXTpuede tener límites distintos.BOOLEANno existe en todos los motores antiguos (en algunos esBIT).
Recomendación general:
- Usa tipos estándar SQL siempre que sea posible (
INT,VARCHAR,DATE,NUMERIC,BOOLEAN). - Evita tipos propietarios si el proyecto debe migrarse en el futuro.
Buenas prácticas
- Usa el tipo más preciso y pequeño posible que represente tu dato.
- Nunca guardes fechas como texto ni números como cadenas.
- Usa
CHECKpara limitar rangos y valores válidos. - Prefiere
NUMERICaFLOATpara dinero. - Usa
VARCHARcon límites razonables (noVARCHAR(5000)“por si acaso”). - Define valores
DEFAULTcoherentes. - Aprovecha los dominios para validaciones recurrentes.
Errores comunes
- Usar
TEXTpara todo (ineficiencia y sin control de longitud). - Usar
FLOATpara montos financieros. - No definir
DEFAULTen campos booleanos o fechas. - No limitar longitudes ni valores válidos.
- Guardar datos con formato ambiguo (fechas o números en texto).