Cuando definís una transformación, tenés que decidir cómo va a persistir el resultado. Esa decisión parece menor, pero tiene implicancias directas en cuánto pagás de cómputo, cuánto tarda en estar disponible el dato, y cuánto esfuerzo vas a poner en mantenerlo.
Las herramientas de transformación como dbt, SQLMesh o Dataform ofrecen varias opciones de materialización. Cada una tiene trade-offs que no siempre son evidentes.
Las opciones básicas
Tabla: cada vez que ejecutás la transformación, se recrea la tabla completa. Borrás todo, volvés a cargar todo. Es la opción más simple y la más predecible.
Vista: no persiste datos. Cada vez que alguien consulta la vista, se ejecuta la query subyacente. No consume almacenamiento, pero puede ser más lenta que una tabla.
Incremental: la tabla se crea una vez, y en ejecuciones sucesivas solo se insertan o actualizan los registros nuevos o modificados. Es más eficiente para volúmenes grandes, pero requiere definir la lógica de incrementalidad.
Efímero: no persiste nada. El código se inyecta como subquery en los modelos que lo referencian. Útil para transformaciones intermedias que no querés que queden como objetos en la base.
Vista materializada: si el motor lo soporta, es una vista que se refresca automáticamente sin necesidad de ejecutar la herramienta de transformación.
El trade-off central: simplicidad vs eficiencia
La tabla es la opción más simple. No tenés que pensar en incrementalidad, no tenés que definir claves únicas, no tenés que preocuparte por duplicados. Cada ejecución es un full refresh: borrás y volvés a cargar.
El problema es que eso funciona bien con pocos datos. Cuando la tabla tiene millones de registros, recrearla completa cada vez se vuelve caro en tiempo y en dinero. En un motor cloud donde pagás por cómputo, cada ejecución es costo.
La materialización incremental resuelve ese problema: solo procesás lo que cambió. Pero a cambio, tenés que definir cómo identificar los registros nuevos o modificados, y cómo manejar los updates. Eso agrega complejidad al modelo.
Cuándo usar cada una
Tabla conviene cuando:
- El volumen de datos es bajo (cientos o miles de registros).
- La transformación es rápida de ejecutar.
- Preferís simplicidad sobre eficiencia.
- Querés garantizar que el resultado siempre refleja el estado actual sin riesgo de inconsistencias.
Vista conviene cuando:
- No querés consumir almacenamiento.
- La query subyacente es rápida.
- Los datos se consultan con poca frecuencia.
- Querés que el resultado siempre esté actualizado sin ejecutar nada.
Incremental conviene cuando:
- El volumen de datos es alto.
- Tenés un campo que permite identificar registros nuevos o modificados (fecha de actualización, por ejemplo).
- El costo de recrear la tabla completa es prohibitivo.
- Podés aceptar la complejidad adicional de definir la lógica de incrementalidad.
Efímero conviene cuando:
- La transformación es un paso intermedio que no tiene valor por sí mismo.
- No querés que quede como objeto en la base de datos.
- Querés reducir la cantidad de objetos a gobernar.
La coherencia con la ingesta
Si la ingesta ya está pensada de forma incremental, tiene sentido que la transformación también lo sea. No es obligatorio, pero desalinear los ritmos entre capas es una fuente típica de problemas.
Cuando la ingesta trae solo los registros nuevos o modificados, y la transformación recrea la tabla completa cada vez, estás desperdiciando el trabajo que hizo la ingesta. Y al revés: si la ingesta hace full refresh pero la transformación es incremental, podés terminar con inconsistencias difíciles de detectar.
La coherencia entre capas no es una ley física, pero facilita el razonamiento sobre el pipeline y reduce las sorpresas.
El costo de la incrementalidad
La materialización incremental tiene costos en términos de complejidad. Tenés que:
- Definir una clave única para identificar registros.
- Definir cómo detectar registros nuevos o modificados (generalmente con un campo de fecha).
- Decidir qué hacer cuando un registro ya existe: update, merge, delete + insert.
- Entender cómo el adaptador de tu herramienta implementa esa lógica en tu motor específico.
Ese último punto es importante. Cuando usás una herramienta como dbt, delegás en el adaptador la implementación del incremental. El adaptador puede usar tablas temporales, merge, delete + insert, o cualquier otra estrategia que el motor soporte. Eso está bien: te permite concentrarte en la lógica de negocio y no en el DDL.
Pero delegar no significa desentenderse. Si algo falla o el rendimiento no es el esperado, tenés que poder revisar qué está haciendo el adaptador por atrás. La herramienta compila tu código y lo manda a ejecutar al motor. Entender qué se ejecuta es parte del trabajo.
El caso de las dimensiones y las facts
En un modelo dimensional, las dimensiones suelen tener pocos registros (miles o decenas de miles). Recrearlas completas cada vez es viable y simple. La materialización como tabla funciona bien.
Las tablas de hechos son otro caso. Pueden tener millones o cientos de millones de registros. Recrearlas completas cada vez es caro. Ahí la materialización incremental tiene más sentido.
Pero hay excepciones. Una dimensión que cambia frecuentemente y tiene muchos registros puede justificar incrementalidad. Una fact pequeña puede funcionar bien como tabla. El volumen y la frecuencia de cambio son los factores que importan, no el tipo de tabla.
El riesgo de la incrementalidad mal implementada
Si la lógica de incrementalidad está mal definida, podés terminar con duplicados o con datos faltantes. Y lo peor es que esos problemas pueden no ser evidentes de inmediato.
Antes de ir a incremental, asegurate de que:
- Tenés un campo confiable para detectar cambios (fecha de actualización, por ejemplo).
- Ese campo se actualiza correctamente en el origen.
- La clave única que definiste es realmente única.
- Tenés tests que validen la integridad de los datos.
Si no tenés certeza sobre alguno de estos puntos, la tabla con full refresh es más segura. Mejor un pipeline más lento pero correcto que uno rápido pero con datos inconsistentes.
En resumen
La elección de materialización depende del volumen de datos, la frecuencia de cambio, el costo de cómputo, y la complejidad que estés dispuesto a aceptar.
Para volúmenes bajos, la tabla con full refresh es la opción más simple y segura. Para volúmenes altos, la materialización incremental es más eficiente pero requiere más cuidado en la implementación.
No hay una respuesta universal. Lo que importa es entender los trade-offs y elegir conscientemente según el caso de uso.