DDL de Particionamiento Declarativo
Las tablas padre/hijo y la exclusión de restricciones permiten a PostgreSQL enrutar inserciones y podar escaneos sin enrutamiento manual basado en disparadores.
Receta
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
CREATE TABLE measurements (
sensor_id integer NOT NULL,
measured_at timestamptz NOT NULL,
value double precision NOT NULL,
PRIMARY KEY (sensor_id, measured_at)
) PARTITION BY RANGE (measured_at);
CREATE TABLE measurements_2026_q1 PARTITION OF measurements
FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');
CREATE TABLE measurements_2026_q2 PARTITION OF measurements
FOR VALUES FROM ('2026-04-01') TO ('2026-07-01');
ALTER TABLE measurements ADD CONSTRAINT measurements_value_check
CHECK (value >= 0);Cuándo usar esto:
- Tablas que exceden las ventanas manejables de vacuum/backup como una única heap.
- Consultas que filtran consistentemente en una clave de partición (tiempo, inquilino, región).
- La política de retención elimina o archiva ventanas de tiempo completas.
Ejemplo de Trabajo
BEGIN;
CREATE TABLE audit_log (
log_id bigint GENERATED ALWAYS AS IDENTITY,
tenant_id bigint NOT NULL,
logged_at timestamptz NOT NULL DEFAULT now(),
action text NOT NULL,
details jsonb,
PRIMARY KEY (log_id, tenant_id, logged_at)
) PARTITION BY RANGE (logged_at);
CREATE TABLE audit_log_2026_h1 PARTITION OF audit_log
FOR VALUES FROM ('2026-01-01') TO ('2026-07-01');
CREATE TABLE audit_log_2026_h2 PARTITION OF audit_log
FOR VALUES FROM ('2026-07-01') TO ('2027-01-01');
CREATE INDEX audit_log_tenant_logged_idx
ON audit_log (tenant_id, logged_at DESC);
-- La inserción se enruta a la hija correcta automáticamente
INSERT INTO audit_log (tenant_id, action, details)
VALUES (42, 'login', '{"ip": "10.0.0.1"}');
-- Inspeccionar el árbol de particiones
SELECT inhrelid::regclass AS partition
FROM pg_inherits
WHERE inhparent = 'audit_log'::regclass;
COMMIT;Lo que esto demuestra:
PARTITION BYdeclarativo en la tabla padre con hijas definidas por límites.- La clave primaria compuesta incluye columnas de clave de partición (
logged_at). - Los índices creados en la tabla padre existen en cada partición.
Inmersión Profunda
Cómo Funciona
- La tabla padre es una tabla particionada: las inserciones y consultas apuntan a la tabla padre; el planificador se expande a las hijas.
- Cada hija hereda las definiciones de columna; los límites se almacenan como restricciones de partición.
- La exclusión de restricciones (poda de particiones) omite las particiones cuyos límites no pueden coincidir con el predicado de la consulta.
- La sintaxis
ONLY parentse dirige a la tabla padre sin hijas para casos extremos de DDL.
Operaciones DDL
| Operación | Propósito |
|---|---|
CREATE TABLE ... PARTITION OF | Añadir una hija con límites |
ATTACH PARTITION | Promover una tabla existente a hija |
DETACH PARTITION | Eliminar una hija como tabla independiente |
SPLIT PARTITION | (no incorporado) - usar detach + nuevas hijas |
Partición DEFAULT | Capturar inserciones no coincidentes |
Notas SQL
-- Plantilla para automatización mensual
DO $$
DECLARE
start_date date := date_trunc('month', now())::date;
end_date date := (start_date + interval '1 month')::date;
part_name text := 'events_' || to_char(start_date, 'YYYY_MM');
BEGIN
EXECUTE format(
'CREATE TABLE IF NOT EXISTS %I PARTITION OF events FOR VALUES FROM (%L) TO (%L)',
part_name, start_date, end_date
);
END;
$$;Trampas
- Clave primaria sin clave de partición: el DDL falla en tablas particionadas. Solución: incluir la columna de partición en las restricciones PK y UNIQUE.
- Límites superpuestos:
attachfalla o causa un enrutamiento ambiguo. Solución: rangos semiabiertos, creación automatizada de particiones. - Demasiadas particiones: miles de particiones diarias perjudican la planificación. Solución: particiones semanales/mensuales; ver las mejores prácticas.
- UNIQUE global sin clave de partición: no soportado entre particiones. Solución: incluir la clave de partición o usar un ID sustituto por ámbito de partición.
- Disparador
FOR EACH ROWsolo en la tabla padre: el comportamiento difiere de las tablas no particionadas. Solución: probar disparadores en cada hija o usar lógica de aplicación.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Tabla grande única + BRIN | Series temporales moderadas, operaciones simples | Necesita desvinculación rápida de retención de particiones |
| Sharding externo a PG | Escala de escritura más allá de un nodo | Se requieren transacciones entre shards |
| Herencia de tablas (legado) | Mantenimiento de código PG antiguo | Greenfield - usar declarativo |
| Tabla separada por mes (manual) | Operaciones personalizadas extremas | Desea una superficie unificada de DDL y consultas |
Preguntas Frecuentes
¿Los datos viven en la tabla padre?
No. Las filas viven en las particiones hijas. La tabla padre es una envoltura de enrutamiento, excepto brevemente durante algunas operaciones de attach.
¿Pueden las claves foráneas referenciar una tabla particionada?
Sí, en PostgreSQL 18: las FK pueden referenciar tablas padre particionadas. Las FK desde tablas particionadas que referencian tablas no particionadas también son compatibles con advertencias sobre ON DELETE entre particiones.
¿Cuántas columnas de clave de partición puedo usar?
Típicamente una columna para rango/lista/hash. El particionamiento de rango mult-columna es compatible con listas de columnas en las definiciones de límites.
¿Qué es la exclusión de restricciones?
El planificador omite las particiones cuyas restricciones CHECK contradicen la cláusula WHERE de la consulta. Es el mecanismo detrás de la poda de particiones.
¿Puedo cambiar la estrategia de partición más tarde?
No en el lugar. Cree una nueva tabla particionada, copie/intercambie o adjunte la estrategia con una ventana de migración.
¿Las particiones necesitan valores predeterminados de columna coincidentes?
Las hijas heredan los valores predeterminados de la tabla padre en el momento de la creación. Modifique los valores predeterminados de la tabla padre y propáguelos cuidadosamente a las nuevas hijas.
¿Cómo funcionan las columnas `GENERATED`?
Se definen en la tabla padre; las hijas las heredan. Las columnas de identidad funcionan por partición con GENERATED BY DEFAULT para flujos de trabajo de attach.
¿Qué pasa con la sub-partición?
PostgreSQL admite jerarquías de particiones (por ejemplo, rango por mes, sub-partición hash por inquilino). Añade complejidad de planificación; úselo cuando se demuestre que es necesario.
¿Se pueden particionar las tablas temporales?
No. El particionamiento se dirige a tablas base persistentes para obtener beneficios de retención y poda.
¿Cómo listo los límites de las particiones?
SELECT c.relname, pg_get_expr(c.relpartbound, c.oid)
FROM pg_class c
JOIN pg_inherits i ON i.inhrelid = c.oid
WHERE i.inhparent = 'audit_log'::regclass;Relacionado
- Conceptos Básicos de Particionamiento - introducción a rango/lista/hash
- Poda de Particiones - comportamiento del planificador
- Retención y Desvinculación - gestión del ciclo de vida
- Trampas de Particionamiento - errores comunes
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, y PostGIS 3.5+.