Instalación de dbt para Transformación de Datos con SQL
dbt (data build tool) es la herramienta estándar del sector para transformar datos en el almacén de datos mediante SQL modular y testeable. Con dbt puedes organizar tus transformaciones en modelos SQL reutilizables, ejecutar tests de calidad de datos de forma automática y generar documentación navegable del linaje de datos. Esta guía cubre la instalación en Linux, configuración de proyectos, creación de modelos y la integración con herramientas de CI/CD.
Requisitos Previos
- Ubuntu 20.04/22.04 o CentOS 8+/Rocky Linux 8+
- Python 3.8 o superior
- pip y virtualenv
- Acceso a un data warehouse: PostgreSQL, Snowflake, BigQuery, Redshift o DuckDB
- Git instalado
Instalación de dbt
# Instalar Python y pip si no están disponibles
sudo apt update && sudo apt install -y python3 python3-pip python3-venv # Ubuntu/Debian
sudo dnf install -y python3 python3-pip # CentOS/Rocky
# Crear entorno virtual dedicado para dbt
python3 -m venv ~/.venv/dbt
source ~/.venv/dbt/bin/activate
# Instalar dbt con el adaptador de tu base de datos
# PostgreSQL / Redshift
pip install dbt-postgres
# Snowflake
pip install dbt-snowflake
# BigQuery
pip install dbt-bigquery
# DuckDB (ideal para desarrollo local)
pip install dbt-duckdb
# Verificar instalación
dbt --version
Para facilitar el uso, añade el entorno al PATH de forma permanente:
echo 'source ~/.venv/dbt/bin/activate' >> ~/.bashrc
source ~/.bashrc
Crear un Proyecto dbt
# Inicializar un nuevo proyecto dbt
dbt init mi_proyecto_dbt
# El asistente preguntará:
# - Nombre del proyecto (ya introducido)
# - Adaptador de base de datos a usar
# - Credenciales de conexión
cd mi_proyecto_dbt
ls -la
Estructura de directorios del proyecto:
mi_proyecto_dbt/
├── dbt_project.yml # Configuración del proyecto
├── profiles.yml # Conexiones a bases de datos (NO en el repo)
├── models/ # Modelos SQL
│ ├── staging/ # Capa de staging (datos crudos limpiados)
│ ├── intermediate/ # Transformaciones intermedias
│ └── marts/ # Tablas finales para BI/analytics
├── tests/ # Tests personalizados
├── macros/ # Funciones SQL reutilizables (Jinja)
├── seeds/ # Datos CSV estáticos
├── snapshots/ # Capturas de datos que cambian con el tiempo
└── analyses/ # Análisis SQL ad-hoc
Archivo dbt_project.yml básico:
name: mi_proyecto_dbt
version: '1.0.0'
config-version: 2
# Variables del proyecto
profile: mi_perfil_postgres
model-paths: ["models"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
# Configuración global de modelos
models:
mi_proyecto_dbt:
# Capa staging: vistas (no materializadas)
staging:
+materialized: view
+schema: staging
# Capa marts: tablas físicas
marts:
+materialized: table
+schema: analytics
Configurar la Conexión (Profiles)
El archivo ~/.dbt/profiles.yml contiene las credenciales de conexión y no debe subirse al repositorio:
# ~/.dbt/profiles.yml
mi_perfil_postgres:
target: dev
outputs:
dev:
type: postgres
host: localhost
port: 5432
user: dbt_user
password: "{{ env_var('DBT_POSTGRES_PASSWORD') }}"
dbname: mi_datawarehouse
schema: dbt_dev
threads: 4
keepalives_idle: 0
prod:
type: postgres
host: postgres-prod.empresa.com
port: 5432
user: dbt_prod
password: "{{ env_var('DBT_POSTGRES_PASSWORD_PROD') }}"
dbname: datawarehouse
schema: analytics
threads: 8
# Probar la conexión
dbt debug
# La salida debería mostrar "All checks passed!"
Modelos SQL
Los modelos son archivos SQL que dbt compila y ejecuta en el data warehouse:
-- models/staging/stg_ventas.sql
-- Limpieza básica de la tabla cruda de ventas
SELECT
id_venta::INTEGER AS id_venta,
TRIM(LOWER(cliente_email)) AS email_cliente,
importe::NUMERIC(10, 2) AS importe,
fecha_venta::DATE AS fecha_venta,
COALESCE(categoria, 'Sin categoría') AS categoria,
CASE
WHEN estado IN ('completada', 'pagada') THEN 'completada'
WHEN estado = 'cancelada' THEN 'cancelada'
ELSE 'pendiente'
END AS estado_normalizado,
_cargado_at AS cargado_en_staging_at
FROM {{ source('raw', 'ventas_raw') }}
WHERE id_venta IS NOT NULL
AND fecha_venta >= '2020-01-01'
-- models/marts/mart_ventas_mensual.sql
-- Agrupación mensual de ventas para dashboards BI
WITH ventas_staged AS (
SELECT * FROM {{ ref('stg_ventas') }}
),
clientes AS (
SELECT * FROM {{ ref('stg_clientes') }}
)
SELECT
DATE_TRUNC('month', v.fecha_venta) AS mes,
v.categoria,
c.segmento_cliente,
COUNT(DISTINCT v.id_venta) AS num_ventas,
SUM(v.importe) AS total_ventas,
AVG(v.importe) AS ticket_medio,
COUNT(DISTINCT v.email_cliente) AS clientes_unicos
FROM ventas_staged v
LEFT JOIN clientes c ON v.email_cliente = c.email
WHERE v.estado_normalizado = 'completada'
GROUP BY 1, 2, 3
# Ejecutar todos los modelos
dbt run
# Ejecutar un modelo específico
dbt run --select stg_ventas
# Ejecutar un modelo y todos sus descendientes
dbt run --select stg_ventas+
# Ejecutar modelos de una carpeta
dbt run --select marts.*
Tests de Calidad de Datos
dbt incluye tests genéricos y permite crear tests personalizados:
# models/staging/schema.yml
version: 2
models:
- name: stg_ventas
description: "Tabla de ventas limpiada desde la fuente raw"
columns:
- name: id_venta
description: "Identificador único de la venta"
tests:
- unique # No hay duplicados
- not_null # No hay nulos
- name: estado_normalizado
tests:
- accepted_values: # Solo valores permitidos
values: ['completada', 'cancelada', 'pendiente']
- name: importe
tests:
- not_null
- dbt_utils.expression_is_true:
expression: ">= 0" # El importe no puede ser negativo
- name: email_cliente
tests:
- not_null
- relationships: # Integridad referencial
to: ref('stg_clientes')
field: email
Test personalizado en tests/assert_ventas_sin_futuro.sql:
-- Test: no debe haber ventas con fecha futura
SELECT *
FROM {{ ref('stg_ventas') }}
WHERE fecha_venta > CURRENT_DATE
# Ejecutar todos los tests
dbt test
# Tests de un modelo específico
dbt test --select stg_ventas
# Ejecutar modelos y tests juntos
dbt build
Documentación
# Generar la documentación del proyecto
dbt docs generate
# Servir la documentación en el navegador (localhost:8080)
dbt docs serve --port 8080
Añade descripciones en los archivos schema.yml:
models:
- name: mart_ventas_mensual
description: >
Agregación mensual de ventas completadas, enriquecida con
el segmento de cliente. Usada por los dashboards de Metabase
y los informes mensuales de dirección.
config:
tags: ['marts', 'ventas', 'mensual']
Modelos Incrementales
Para tablas grandes donde no conviene recalcular todo cada vez:
-- models/marts/mart_eventos_incremental.sql
{{
config(
materialized='incremental',
unique_key='id_evento',
incremental_strategy='delete+insert'
)
}}
SELECT
id_evento,
usuario_id,
tipo_evento,
propiedades,
ocurrido_en
FROM {{ source('raw', 'eventos') }}
{% if is_incremental() %}
-- Solo procesar registros nuevos desde la última ejecución
WHERE ocurrido_en > (SELECT MAX(ocurrido_en) FROM {{ this }})
{% endif %}
CI/CD con dbt
Integración con GitHub Actions para ejecutar dbt en cada Pull Request:
# .github/workflows/dbt-ci.yml
name: dbt CI
on:
pull_request:
branches: [main]
jobs:
dbt-ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Instalar dbt
run: pip install dbt-postgres
- name: Crear profiles.yml desde secrets
run: |
mkdir -p ~/.dbt
cat > ~/.dbt/profiles.yml <<EOF
mi_perfil_postgres:
target: ci
outputs:
ci:
type: postgres
host: ${{ secrets.DB_HOST }}
port: 5432
user: ${{ secrets.DB_USER }}
password: ${{ secrets.DB_PASSWORD }}
dbname: datawarehouse_ci
schema: dbt_ci_${{ github.event.pull_request.number }}
threads: 4
EOF
- name: Verificar conexión
run: dbt debug
- name: Ejecutar modelos y tests
run: dbt build --target ci
env:
DBT_POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}
Solución de Problemas
Error "Could not find profile named X":
# Verificar que el perfil en dbt_project.yml coincide con profiles.yml
grep "profile:" dbt_project.yml
cat ~/.dbt/profiles.yml | grep -A1 "outputs:"
# Especificar perfil explícitamente
dbt run --profile mi_perfil_postgres
Error de compilación en un modelo:
# Ver el SQL compilado para depurar
dbt compile --select nombre_modelo
cat target/compiled/mi_proyecto/models/nombre_modelo.sql
Tests que fallan:
# Ver los registros que fallan en un test
dbt test --select nombre_modelo --store-failures
# Los registros que fallan se guardan en el schema de fallos configurado
Modelos lentos:
# Ver tiempos de ejecución
dbt run --select nombre_modelo
# Revisar el output para ver el tiempo de cada modelo
# Considerar:
# 1. Añadir índices en las columnas de JOIN
# 2. Cambiar materialización a incremental
# 3. Aumentar el número de threads en profiles.yml
Conclusión
dbt se ha convertido en el estándar de facto para la transformación de datos en almacenes modernos gracias a su enfoque en SQL puro, los tests automáticos de calidad y la documentación integrada del linaje de datos. Su integración con herramientas de CI/CD permite tratar las transformaciones de datos como código, con revisiones de código, tests automáticos y despliegues controlados. Comenzar con dbt en un proyecto nuevo es sencillo, y su adopción mejora significativamente la mantenibilidad y fiabilidad de los pipelines de datos.


