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

Cómo crear pipelines ELT para anonimizar PII: paso a paso

João Barros 15 de August de 2026 3 min de lectura

Aprende a anonimizar PII en ELT de forma práctica y segura: este tutorial describe por qué es importante tratar PII (Personally Identifiable Information), qué técnicas usar y cómo integrar esas transformaciones en un pipeline ELT para mantener los datos útiles para análisis sin exponer información sensible. Explicaré el motivo de cada elección y daré pasos concretos con ejemplos SQL que funcionan en motores compatibles (Databricks, Synapse, Snowflake).

Pre-requisitos

  • Cuenta o entorno con un motor SQL/Delta compatible (p. ej.: Databricks, Synapse, Snowflake). Idealmente con 4–8 nodos para probar rendimiento en conjuntos de 1–10M registros.
  • Fuente de datos con PII (CSV/JSON) accesible al entorno; un archivo de prueba con ~1000 filas ayuda a validar antes del rollout.
  • Permisos para crear tablas, funciones y ejecutar jobs ELT; acceso a un servicio de tokenización si existe la necesidad de reversión.
  • Conocimientos básicos de SQL y funciones de hashing/cryptography. Conocimientos de MERGE/UPSERT y particionamiento son útiles para pipelines idempotentes.

Paso 1: Identificar campos PII y definir requisitos de anonimización

Antes de codificar, haz un inventario de los campos PII (p. ej.: name, email, NIF, móvil). Para cada campo decide el método adecuado: enmascaramiento simple (mantiene parte legible), hashing irreversible (SHA256) cuando no necesitas revertir, tokenización cuando se necesita reversión controlada, o generalización cuando solo es necesaria agregación (p. ej.: almacenar solo el prefijo del móvil).

Ejemplo de mapeo para 5 campos:

  • name: enmascaramiento (primeros 2 caracteres) — preserva segmentación por iniciales en informes;
  • email: mantener dominio, hashear el user — útil para mantener métricas por dominio (p. ej.: gmail.com) sin exponer el user;
  • NIF: hashing irreversible — obligatorio para impedir reconstrucción;
  • móvil: generalización al código nacional (3 dígitos) — suficiente para análisis por región;
  • created_at: mantener intacto para análisis temporales.
Definir requisitos legales también: por ejemplo, la retención de PII puede estar limitada a 2 años — documenta esto en el plan de gobernanza.

Paso 2: Crear tabla staging non-anonymized

Carga los datos originales a una tabla staging sin modificaciones. Esto permite auditoría, reconciliación y reprocesado. Mantén el staging en una zona segura (p. ej.: OneLake/secure storage) con control de acceso restringido al equipo responsable.

-- Exemplo SQL para Databricks/Synapse/Snowflake
CREATE OR REPLACE TABLE raw_customers_staging (
  id STRING,
  name STRING,
  email STRING,
  nif STRING,
  phone STRING,
  created_at TIMESTAMP
);

-- COPY/LOAD a partir de CSV ou caminho de origem (varia por plataforma)
-- Para grandes volumes recomenda-se particionar por created_at ou por dia.

Paso 3: Implementar funciones de anonimización reutilizables

Crea UDFs/SQL functions para que la lógica esté centralizada, testeable y auditable. Esto facilita actualizaciones (p. ej.: cambiar algoritmo de hashing) sin alterar múltiples queries. Testea las funciones con conjuntos de 1000–10.000 registros antes de usar en producción.

-- Hash irreversível (SHA256) e truncar para uma coluna identificadora
CREATE OR REPLACE FUNCTION hash_sha256(val STRING) RETURNS STRING AS (
  sha2(val, 256)
);

-- Mascaramento: manter primeiros 2 caracteres e substituir o resto por X
CREATE OR REPLACE FUNCTION mask_name(val STRING) RETURNS STRING AS (
  CASE WHEN val IS NULL THEN NULL
       WHEN length(val) <= 2 THEN repeat('X', length(val))
       ELSE substr(val,1,2) || repeat('X', length(val)-2)
  END
);

-- Generalizar telemóvel: manter só prefixo nacional (ex.: primeiros 3 dígitos)
CREATE OR REPLACE FUNCTION generalize_phone(val STRING) RETURNS STRING AS (
  CASE WHEN val IS NULL THEN NULL
       WHEN length(regexp_replace(val,'\\D','')) <= 3 THEN 'REDACTED'
       ELSE substr(regexp_replace(val,'\\D',''),1,3) || 'XXXXX'
  END
);

Paso 4: Anonimizar con una query ELT y guardar en tabla target

Aplica las funciones en la transformación y escribe el resultado en la tabla final que se usará para informes. Mantén columnas no sensibles intactas y documenta claramente qué campos fueron transformados. Si necesitas revertir, integra un servicio de tokenización (gestionado y auditado) en lugar de hashing.

CREATE OR REPLACE TABLE customers_anonymized AS
SELECT
  id,
  mask_name(name) AS name_masked,
  -- manter domínio: split email e hash o user
  concat(hash_sha256(split_part(email,'@',1)), '@', split_part(email,'@',2)) AS email_anonymized,
  -- NIF hashed para irreversibilidade
  hash_sha256(nif) AS nif_hash,
  generalize_phone(phone) AS phone_generalized,
  created_at
FROM raw_customers_staging;

Paso 5: Automatizar ELT y validar idempotencia

Programa el job para ejecutarse periódicamente (p. ej.: cada hora o nightly). Asegura idempotencia usando MERGE/UPSERT y watermarking (por ejemplo, procesar solo records con created_at >= ultima_run). Testea con reruns: al recrear el pipeline 3 veces seguidas el número de registros en la tabla target debe permanecer estable.

-- Exemplo MERGE para atualização incremental
MERGE INTO customers_anonymized tgt
USING (SELECT * FROM raw_customers_staging WHERE created_at >= date_sub(current_date(),1)) src
ON tgt.id = src.id
WHEN MATCHED THEN UPDATE SET
  name_masked = mask_name(src.name),
  email_anonymized = concat(hash_sha256(split_part(src.email,'@',1)), '@', split_part(src.email,'@',2)),
  nif_hash = hash_sha256(src.nif),
  phone_generalized = generalize_phone(src.phone),
  created_at = src.created_at
WHEN NOT MATCHED THEN INSERT VALUES (
  src.id,
  mask_name(src.name),
  concat(hash_sha256(split_part(src.email,'@',1)), '@', split_part(src.email,'@',2)),
  hash_sha256(src.nif),
  generalize_phone(src.phone),
  src.created_at
);

Verificar el resultado

Realiza comprobaciones automáticas y manuales: asegúrate de que no existen campos originales en la tabla anonymizada, que los formatos son consistentes y que el hashing tiene la longitud esperada (SHA256 → 64 hex chars). Ejemplos de queries de validación y métricas:

-- Amostra de 5 linhas para inspeção visual
SELECT id, name_masked, email_anonymized, phone_generalized FROM customers_anonymized LIMIT 5;

-- Verifica que nif_hash tem comprimento 64 (SHA256 hex)
SELECT DISTINCT length(nif_hash) AS len FROM customers_anonymized LIMIT 5;

-- Conta linhas para garantir idempotência após rerun
SELECT count(*) FROM customers_anonymized;

-- Percentagem de NULLs por coluna para validar qualidade
SELECT
  sum(CASE WHEN name_masked IS NULL THEN 1 ELSE 0 END)/count(*) AS pct_null_name,
  sum(CASE WHEN email_anonymized IS NULL THEN 1 ELSE 0 END)/count(*) AS pct_null_email
FROM customers_anonymized;

Conclusión

Anonimizar PII en ELT reduce riesgo y permite análisis útiles. Empieza pequeño (p. ej.: 10k registros) para validar corrección y rendimiento antes de escalar a millones de filas. Próximos pasos prácticos: implementar tokenización reversible si es necesario, añadir logging y auditoría (quién procesó el job, cuándo), y probar el rendimiento con datos en batch y streaming. Y, por supuesto, confirma requisitos legales y políticas internas de gobernanza antes del roll-out.