(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa
ETL

Idempotencia en ETL: reejecutar sin duplicar datos

João Barros 04 de July de 2026 5 min de lectura

Un pipeline ETL idempotente es aquel que puedes ejecutar dos, cinco o diez veces seguidas y siempre obtienes el mismo resultado final, sin filas duplicadas ni datos corrompidos. Esta propiedad es la que te deja dormir tranquilo: cuando un proceso falla a mitad y hay que reprocesarlo, la idempotencia garantiza que el segundo intento no estropea lo que el primero ya había cargado. En producción, los fallos de red, los timeouts y los reinicios ocurren, y un pipeline idempotente convierte esos sustos en una simple nueva ejecución. A continuación verás, paso a paso, cómo transformar un Load de "append ciego" en un Load idempotente usando una clave de negocio y un MERGE.

Requisitos previos

  • Un destino relacional (por ejemplo SQL Server, Azure SQL o PostgreSQL) donde crear la tabla final.
  • Nociones básicas de SQL: INSERT, UPDATE e, idealmente, MERGE.
  • Un origen con un identificador estable por registro (por ejemplo, el id de la venta).

Paso 1: Entender por qué un Load simple no es idempotente

El patrón más común en la fase de Load es un INSERT directo desde la staging hacia el destino. El problema es que cada ejecución añade filas: si el pipeline falla después de cargar la mitad de las ventas y vuelves a ejecutarlo, las filas ya cargadas entran otra vez y acabas con registros repetidos. Imagina un informe de facturación contando la misma venta dos veces: el error se propaga a todo el análisis. Un Load idempotente depende solo de lo que hay en el origen, no de cuántas veces se ejecutó.

Idempotencia en ETL: reejecutar sin duplicar datos
INSERT INTO vendas_dest (id_venda, cliente, total)
SELECT id_venda, cliente, total
FROM vendas_stg;

Paso 2: Elegir una clave de negocio estable

La clave de negocio es la columna (o conjunto de columnas) que identifica un registro de forma única y que no cambia con el tiempo, como un id_venda. Esta clave es la que permite decidir, para cada fila, si debes insertar un registro nuevo o actualizar uno existente, en lugar de insertar siempre. Si necesitas más de una columna para garantizar la unicidad, usa una clave compuesta.

Paso 3: Sustituir el INSERT por un MERGE (upsert)

MERGE compara el origen con el destino por la clave: si la fila ya existe, hace UPDATE; si no existe, hace INSERT. Como es una única instrucción atómica, o se ejecuta por completo o no cambia nada. Ejecutar el mismo MERGE dos veces con el mismo origen produce exactamente el mismo estado final, que es la definición práctica de idempotencia.

MERGE INTO vendas_dest AS d
USING vendas_stg AS s
   ON d.id_venda = s.id_venda
WHEN MATCHED THEN
   UPDATE SET d.cliente = s.cliente,
              d.total   = s.total
WHEN NOT MATCHED THEN
   INSERT (id_venda, cliente, total)
   VALUES (s.id_venda, s.cliente, s.total);
Consejo: en PostgreSQL, el equivalente es INSERT ... ON CONFLICT ... DO UPDATE; en motores sin MERGE, el patrón delete-insert del Paso 5 siempre funciona.

Paso 4: Asegurarte de que el origen no trae duplicados

MERGE da error si el origen tiene la misma clave en varias filas, porque no sabe cuál usar. Por eso, limpia los duplicados en la staging antes del MERGE, conservando solo la versión más reciente de cada clave con ROW_NUMBER().

WITH ranked AS (
   SELECT id_venda, cliente, total,
          ROW_NUMBER() OVER (
             PARTITION BY id_venda
             ORDER BY data_atualizacao DESC
          ) AS rn
   FROM vendas_raw
)
INSERT INTO vendas_stg (id_venda, cliente, total)
SELECT id_venda, cliente, total
FROM ranked
WHERE rn = 1;

Paso 5: Carga por partición, una alternativa idempotente

Cuando reprocesas una porción entera de datos (por ejemplo, un día), hay un patrón aún más simple: borrar esa partición en el destino y volver a insertarla. Dentro de una transacción, la operación es atómica y puede repetirse sin riesgo.

BEGIN TRANSACTION;

DELETE FROM vendas_dest
WHERE data_venda = '2026-07-04';

INSERT INTO vendas_dest (id_venda, cliente, total, data_venda)
SELECT id_venda, cliente, total, data_venda
FROM vendas_stg
WHERE data_venda = '2026-07-04';

COMMIT;

Verificar el resultado

La prueba real es simple: ejecuta el pipeline dos veces seguidas y confirma que el número de filas y los totales no cambian de la primera a la segunda ejecución. Luego busca claves repetidas en el destino; si esta consulta no devuelve nada, el Load es idempotente.

SELECT id_venda, COUNT(*) AS n
FROM vendas_dest
GROUP BY id_venda
HAVING COUNT(*) > 1;

Conclusión

Con una clave de negocio, un MERGE (o un delete-insert por partición) y una limpieza de duplicados en la staging, tu Load pasa a ser seguro para repetir tantas veces como necesites. A partir de aquí, envuelve todo en una transacción, añade logging y combina la idempotencia con la carga incremental para pipelines rápidos y fiables. La próxima vez que un pipeline falle a mitad, hazte una pregunta: ¿puedo simplemente ejecutarlo otra vez sin miedo? Si la respuesta es sí, ya tienes un ETL idempotente.

Compartir: