Skip to main content

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

TipoTamañoRango aproximadoUso típico
SMALLINT2 bytes-32.768 a 32.767IDs pequeños
INT o INTEGER4 bytes±2.000 millonesClaves comunes
BIGINT8 bytes±9 cuatrillonesClaves globales o contadores grandes
SERIAL / BIGSERIALauto-incrementosegún baseIdentificadores 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

TipoPrecisiónCaracterísticas
NUMERIC(p,s)Precisión exacta, configurableIdeal para dinero o valores críticos
REAL / FLOATAproximado (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

TipoCaracterísticasUso recomendado
CHAR(n)Longitud fijaCódigos o abreviaturas (e.g., “ES”, “US”)
VARCHAR(n)Longitud variable con límiteNombres, correos, direcciones
TEXTSin límite fijoComentarios, 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 TEXT solo para campos donde la longitud es impredecible (observaciones, mensajes, etc.).

Tipos de fecha y hora

TipoDescripciónEjemplo
DATESolo fecha (año-mes-día)'2025-10-19'
TIMESolo hora'14:35:00'
TIMESTAMPFecha + hora'2025-10-19 14:35:00'
TIMESTAMPTZFecha + hora con zona horaria'2025-10-19 14:35:00+02'
INTERVALDuració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ógicoSQL estándarAlternativas (según motor)
VerdaderoTRUE o 1't', 'yes'
FalsoFALSE 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 INT con 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 DEFAULT para 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.
  • TEXT puede tener límites distintos.
  • BOOLEAN no existe en todos los motores antiguos (en algunos es BIT).

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 CHECK para limitar rangos y valores válidos.
  • Prefiere NUMERIC a FLOAT para dinero.
  • Usa VARCHAR con límites razonables (no VARCHAR(5000) “por si acaso”).
  • Define valores DEFAULT coherentes.
  • Aprovecha los dominios para validaciones recurrentes.

Errores comunes

  • Usar TEXT para todo (ineficiencia y sin control de longitud).
  • Usar FLOAT para montos financieros.
  • No definir DEFAULT en campos booleanos o fechas.
  • No limitar longitudes ni valores válidos.
  • Guardar datos con formato ambiguo (fechas o números en texto).