PostgreSQL
Arrays, ranges, domains y tipos avanzados
Tipos avanzados de PostgreSQL como arrays, ranges, multiranges, domains, enums y composite types, con operadores, constraints e índices.
- Última actualización
- Actualizada
- Nivel
- Aplicación
PostgreSQL
Tipos avanzados de PostgreSQL como arrays, ranges, multiranges, domains, enums y composite types, con operadores, constraints e índices.
Los tipos avanzados aportan cuando expresan operaciones reales del dominio. Si solo reducen tablas a estructuras compactas pero eliminan integridad, terminan haciendo el sistema más difícil de consultar y evolucionar.
PostgreSQL ofrece arrays, enums, domains, composite types, ranges, multiranges y tipos especializados. La pregunta no es “¿puedo usarlo?”, sino:
¿qué regla expresa?
¿qué operaciones necesito?
¿qué integridad pierdo o gano?
¿cómo lo entiende el driver?CREATE TABLE products (
id bigint PRIMARY KEY,
tags text[] NOT NULL DEFAULT '{}'
);Operaciones:
WHERE 'featured' = ANY(tags)
WHERE tags @> ARRAY['featured']
WHERE tags && ARRAY['featured','seasonal']
SELECT unnest(tags)Un GIN puede acelerar membership y overlap:
CREATE INDEX products_tags_gin_idx
ON products USING gin(tags);Ejemplo: member_ids uuid[] es peor que team_members(team_id, user_id, role, joined_at).
Los arrays de PostgreSQL pueden tener lower bounds distintos de 1, aunque los arrays creados normalmente empiezan en 1. No expongas supuestos de índice al cliente sin contrato.
array_length(tags, 1)
cardinality(tags)cardinality devuelve cantidad total de elementos; array_length trabaja por dimensión.
NULL → no existe array
'{}' → array existe y está vacío
{NULL} → array con un elemento NULLSon estados diferentes. Define NOT NULL DEFAULT '{}' cuando el dominio necesita una colección siempre existente.
CREATE TYPE order_status AS ENUM (
'pending', 'confirmed', 'cancelled'
);Ventajas:
Costes:
Alternativas:
status text NOT NULL CHECK (status IN (...))o tabla catálogo con FK cuando los estados tienen metadata.
CREATE DOMAIN positive_money AS numeric(14,2)
CHECK (VALUE >= 0);Reutiliza tipo y constraints:
price positive_money NOT NULLÚtil cuando la regla es realmente global. No lo uses para reglas que cambian por contexto: “saldo puede ser negativo en ciertas cuentas” no encaja en un domain universal.
Cambiar un domain afecta múltiples columnas y puede validar gran volumen.
CREATE TYPE money_amount AS (
amount numeric(14,2),
currency char(3)
);Pueden usarse como valores o retornos de funciones. Trade-offs:
Son útiles para contratos internos muy estables, no para reemplazar entidades.
CREATE TABLE reservations (
room_id bigint NOT NULL,
reserved_during tstzrange NOT NULL
);Los límites pueden ser inclusivos o exclusivos:
[ inicio incluido
( inicio excluido
] final incluido
) final excluidoConvención frecuente:
[inicio, fin)Permite intervalos consecutivos sin solaparse.
reserved_during @> now()
reserved_during && $1::tstzrange
reserved_during -|- $1::tstzrange@> containment.&& overlap.-|- adyacencia.GiST o SP-GiST pueden indexar estas operaciones según tipo/operator class.
Evitar reservas solapadas:
CREATE EXTENSION IF NOT EXISTS btree_gist;
ALTER TABLE reservations
ADD CONSTRAINT reservations_no_overlap
EXCLUDE USING gist (
room_id WITH =,
reserved_during WITH &&
);Esto protege la regla bajo concurrencia. Un SELECT previo no basta porque dos transacciones pueden no verse.
Para permitir estados cancelados, usa constraint parcial cuando la versión y sintaxis aplicable lo permitan mediante índice/exclusion apropiado o separa modelo.
Representan varios intervalos no solapados:
int4multirange
nummultirange
datemultirange
tstzmultirangeSon útiles para disponibilidad fragmentada, pero pueden ser difíciles de editar y exponer. Si cada intervalo tiene metadata propia, usa tabla hija.
client_ip inet
network cidrPermiten validación y operadores:
client_ip << '10.0.0.0/8'::cidrSon superiores a text para redes, auditoría y reglas de acceso.
public_id uuid NOT NULL DEFAULT gen_random_uuid()Ventajas: generación distribuida y exposición pública. Costes: índice más grande que bigint y localidad variable. No lo uses como sustituto automático de una key de negocio.
El tipo money depende de locale y tiene precisión fija. Para aplicaciones internacionales suele ser más explícito:
amount numeric(14,2) NOT NULL,
currency char(3) NOT NULLLa moneda necesita reglas propias; no sumes currencies distintas.
PostgreSQL core incluye tipos geométricos básicos. PostGIS añade GIS serio: SRID, geography, spatial indexes y funciones especializadas. PostGIS es extensión externa, no parte del core.
Reescribir un elemento puede reescribir todo el valor y generar TOAST/WAL.
La app nueva puede escribir un valor que la app vieja no entiende.
Puede bloquear o validar muchas tablas dependientes.
Puede tener límites sin valor. La aplicación debe entenderlos.
tstzrange representa instantes; la UI debe convertir a zona de negocio.
Puede producir conflictos concurrentes que deben mapearse como error de dominio.
colección pequeña sin identidad
→ array
estado cerrado y estable
→ enum o CHECK
regla de tipo global
→ domain
intervalo con operaciones de overlap
→ range
varios intervalos sin metadata
→ multirange
relación con atributos/FK
→ tabla[inicio, fin)?Full-text search y búsqueda textual diferencia lexemes, substrings, similitud y relevancia.