Tablas particionadas en Oracle (II): hash e interval
La segunda entrega deja lo manual atrás: particionado hash para claves sin orden, e interval, la mejora de 11g que crea las particiones sola al insertar.
La primera parte dejó las cosas claras: range para lo temporal, list para las categorías. Pero quedaban dos esquinas: las tablas cuya clave no tiene ningún orden natural —clientes, sesiones, dispositivos— y la tarea ingrata de ir creando particiones a mano cada mes. Las dos respuestas de Oracle se llaman hash e interval, y juntas cubren casi todo el mapa del particionado moderno.
Hash: repartir sin pensar
El particionado por hash aplica una función de dispersión a la clave y reparte las filas en un número fijo de particiones. No hay semántica: no puedes preguntar «dame la partición de agosto», pero sí garantizar repartos equilibrados y que cada consulta por clave exacta visite una sola pieza:
CREATE TABLE sesiones (
id NUMBER,
usuario_id NUMBER NOT NULL,
inicio DATE,
dispositivo VARCHAR2(60)
)
PARTITION BY HASH (usuario_id)
PARTITIONS 8
STORE IN (ts_datos_1, ts_datos_2);La regla de oro del hash: elige un número de particiones y no lo toques. Añadir particiones después (ADD PARTITION) mueve filas entre todas las piezas —en versiones recientes hay rehashing en línea, pero sigue siendo una operación mayor—. Por eso se recomienda una potencia de dos: el reparto interno trabaja con bits y las mitades quedan limpias. La columna de partición debe ser prácticamente siempre la clave primaria o la columna por la que se busca: hash sobre usuario_id hace que «todas las sesiones del usuario 42» caigan en una única partición.
Interval: el fin de los scripts de mantenimiento
Hasta 11g, toda tabla range tenía su script nocturno creando la partición del mes siguiente, y toda sorpresa era un ORA-14400 en producción a primeras de la mañana. El particionado interval lo elimina: defines la primera partición y un intervalo, y la base de datos crea las demás cuando llegan filas del periodo:
CREATE TABLE eventos (
id NUMBER,
creado DATE NOT NULL,
carga VARCHAR2(200)
)
PARTITION BY RANGE (creado)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
( PARTITION p_inicial VALUES LESS THAN (DATE '2026-01-01') );A partir de aquí, insertar un evento de septiembre de 2026 crea sobre la marcha la partición que lo cubre, con nombre generado por el sistema. Para series más finas —telemetría, logs— el intervalo diario es igual de simple: INTERVAL (NUMTODSINTERVAL(1, 'DAY')). La filosofía cambia de gestión a declaración: describes la cadencia y el motor hace el resto, siempre dentro de los límites del tablespace.
Detalles que importan con interval
- La transacción que crea la partición la bloquea: dos cargas simultáneas del mismo mes pueden pelearse por ella; en cargas paralelas conviene un calentamiento que inserte y haga rollback de una fila del periodo.
- Borrar sigue siendo manual: interval automatiza la creación, no la purga; para retenciones, un
DROP PARTITIONprogramado de las particiones más viejas sigue siendo la práctica común. - Conversión: una tabla range existente se convierte con
ALTER TABLE ... SET INTERVAL(...), sin reorganizar nada.
Combinar estrategias: partición y subpartición
Los esquemas reales suelen ser mestizos: range mensual por fuera, hash por dentro, de modo que cada mes reparte sus claves activas entre ocho subparticiones:
CREATE TABLE metricas (
dia DATE NOT NULL,
dispositivo NUMBER NOT NULL,
valor NUMBER
)
PARTITION BY RANGE (dia)
INTERVAL (NUMTODSINTERVAL(1, 'DAY'))
SUBPARTITION BY HASH (dispositivo)
SUBPARTITIONS 8
( PARTITION p_inicial VALUES LESS THAN (DATE '2026-01-01') );Así, la consulta de «ayer, dispositivo 7» poda la partición del día por fecha y una subpartición por hash: dos podas encadenadas. El plan lo cuenta en las columnas PSTART/PSTOP, que en este caso muestran pares por nivel. Inspeccionar la estructura completa es cosa de una consulta a USER_TAB_PARTITIONS y USER_TAB_SUBPARTITIONS, pareja habitual de las consultas útiles del diccionario.
Guía rápida de elección
- Datos con ciclo de vida temporal: range o interval sobre la fecha; purga con
DROP PARTITIONy, si hay política de retención, PURGE para la papelera que dejan los drops. - Clave sin orden y acceso por identidad: hash sobre la clave, potencia de dos, congelada para siempre.
- Categorías estables (país, región): list con
DEFAULTde red de seguridad.
Con la tabla bien troceada, las operaciones de carga ganan un aliado evidente: el MERGE por partición descrito en el artículo del upsert con MERGE INTO, y los traslados completos de esquema con Data Pump.