Pipeline ejecutado correctamente

Proyecto dbt en acción:
Del CSV al Data Mart

Showcase interactivo de un proyecto dbt real con DuckDB. Explora el lineage graph, inspecciona cada modelo SQL y visualiza los resultados de las transformaciones sobre un dataset de ventas ficticio.

6
Modelos dbt
50
Pedidos
21
Tests PASS
1.02s
dbt run

1Lineage Graph interactivo

El DAG muestra cómo fluyen los datos desde los seeds CSV hasta los data marts finales. Haz clic en cualquier nodo para ver sus detalles y dependencias.

Seeds (raw) Staging (view) Intermediate (view) Marts (table)
raw_customers 10 rows · seed raw_orders 50 rows · seed raw_products 12 rows · seed stg_customers view · staging stg_orders view · staging stg_products view · staging int_orders_enriched view · intermediate fct_orders table · marts dim_customers table · marts

fct_orders

Tipo: table Schema: marts Dependencias: 1 Filas: 50
Tabla de hechos con todos los pedidos y sus métricas financieras. Fuente principal para análisis de ventas.

2Modelos SQL

Los 6 modelos dbt organizados en 3 capas siguiendo las mejores prácticas. Cambia de pestaña para inspeccionar cada transformación.

models/staging/stg_orders.sql
with source as (
    select * from {{ ref('raw_orders') }}
),

renamed as (
    select
        order_id,
        customer_id,
        product_id,
        cast(order_date as date) as order_date,
        quantity,
        discount_pct / 100.0 as discount_rate,
        lower(status) as status,
        lower(channel) as channel,
        current_timestamp as _loaded_at
    from source
)

select * from renamed
models/staging/stg_customers.sql
with source as (
    select * from {{ ref('raw_customers') }}
),

renamed as (
    select
        customer_id,
        first_name,
        last_name,
        first_name || ' ' || last_name as full_name,
        lower(email) as email,
        country,
        city,
        cast(signup_date as date) as signup_date,
        upper(segment) as segment,
        current_timestamp as _loaded_at
    from source
)

select * from renamed
models/staging/stg_products.sql
with source as (
    select * from {{ ref('raw_products') }}
),

renamed as (
    select
        product_id,
        product_name,
        category,
        subcategory,
        price,
        cost,
        price - cost as gross_margin,
        round((price - cost) / price * 100, 2) as margin_pct,
        supplier,
        current_timestamp as _loaded_at
    from source
)

select * from renamed
models/intermediate/int_orders_enriched.sql
with orders as (
    select * from {{ ref('stg_orders') }}
),

customers as (
    select * from {{ ref('stg_customers') }}
),

products as (
    select * from {{ ref('stg_products') }}
),

enriched as (
    select
        o.order_id,
        o.order_date,
        date_trunc('month', o.order_date) as order_month,
        date_trunc('quarter', o.order_date) as order_quarter,
        extract(year from o.order_date) as order_year,

        -- Customer info
        o.customer_id,
        c.full_name as customer_name,
        c.segment as customer_segment,
        c.country as customer_country,
        c.city as customer_city,

        -- Product info
        o.product_id,
        p.product_name,
        p.category as product_category,
        p.subcategory as product_subcategory,

        -- Order details
        o.quantity,
        o.discount_rate,
        o.status,
        o.channel,

        -- Financial calculations
        p.price as unit_price,
        p.cost as unit_cost,
        p.price * o.quantity as gross_revenue,
        p.price * o.quantity * (1 - o.discount_rate) as net_revenue,
        p.cost * o.quantity as total_cost,
        p.price * o.quantity * (1 - o.discount_rate) - p.cost * o.quantity as gross_profit,

        -- Margin
        case
            when p.price * o.quantity * (1 - o.discount_rate) > 0
            then round(
                (p.price * o.quantity * (1 - o.discount_rate) - p.cost * o.quantity)
                / (p.price * o.quantity * (1 - o.discount_rate)) * 100, 2
            )
            else 0
        end as margin_pct

    from orders o
    left join customers c on o.customer_id = c.customer_id
    left join products p on o.product_id = p.product_id
)

select * from enriched
models/marts/fct_orders.sql
with enriched_orders as (
    select * from {{ ref('int_orders_enriched') }}
),

final as (
    select
        order_id,
        order_date,
        order_month,
        order_quarter,
        order_year,

        customer_id,
        customer_name,
        customer_segment,
        customer_country,

        product_id,
        product_name,
        product_category,

        quantity,
        discount_rate,
        status,
        channel,

        unit_price,
        unit_cost,
        gross_revenue,
        net_revenue,
        total_cost,
        gross_profit,
        margin_pct,

        -- Flags
        case when status = 'completed' then true else false end as is_completed,
        case when discount_rate > 0 then true else false end as has_discount,
        case when channel = 'online' then true else false end as is_online

    from enriched_orders
)

select * from final
models/marts/dim_customers.sql
with customers as (
    select * from {{ ref('stg_customers') }}
),

orders as (
    select
        customer_id,
        count(*) as total_orders,
        count(case when status = 'completed' then 1 end) as completed_orders,
        sum(case when status = 'completed' then net_revenue else 0 end) as total_revenue,
        sum(case when status = 'completed' then gross_profit else 0 end) as total_profit,
        min(order_date) as first_order_date,
        max(order_date) as last_order_date,
        avg(case when status = 'completed' then net_revenue end) as avg_order_value
    from {{ ref('int_orders_enriched') }}
    group by 1
),

final as (
    select
        c.customer_id,
        c.full_name,
        c.email,
        c.country,
        c.city,
        c.segment,
        c.signup_date,

        coalesce(o.total_orders, 0) as total_orders,
        coalesce(o.completed_orders, 0) as completed_orders,
        coalesce(o.total_revenue, 0) as lifetime_revenue,
        coalesce(o.total_profit, 0) as lifetime_profit,
        o.first_order_date,
        o.last_order_date,
        coalesce(o.avg_order_value, 0) as avg_order_value,

        case
            when coalesce(o.total_revenue, 0) >= 2000 then 'VIP'
            when coalesce(o.total_revenue, 0) >= 1000 then 'High Value'
            when coalesce(o.total_revenue, 0) >= 500 then 'Medium Value'
            else 'Low Value'
        end as value_tier

    from customers c
    left join orders o on c.customer_id = o.customer_id
)

select * from final

3Resultados de las queries

Datos reales extraídos de DuckDB tras ejecutar dbt run. Explora las tablas finales del data mart.

order_idorder_datecustomer_nameproduct_namecategoryqtynet_revenueprofitmarginstatus
10012023-01-05Ana GarcíaLaptop Pro 15Electrónica11299.99519.9940.0%completed
10022023-01-08Carlos LópezMouse InalámbricoElectrónica256.9832.9857.9%completed
10032023-01-12María MartínezMonitor 27 4KElectrónica1499.99219.9944.0%completed
10042023-01-15Ana GarcíaTeclado MecánicoElectrónica180.9935.9944.4%completed
10052023-01-20Juan RodríguezSilla ErgonómicaMobiliario1399.99219.9955.0%completed
10062023-02-02Laura GonzálezLaptop Pro 15Electrónica11104.99324.9929.4%completed
10072023-02-10Pedro SánchezWebcam HDElectrónica179.9944.9956.2%completed
10082023-02-14Sofía FernándezMouse InalámbricoElectrónica385.4749.4757.9%completed
10092023-02-18Miguel TorresAuriculares BTElectrónica1149.9979.9953.3%completed
10102023-02-25Carmen DíazHub USB-CElectrónica299.9859.9860.0%completed
10112023-03-01Roberto RuizEscritorio StandingMobiliario1629.99279.9944.4%completed
10122023-03-05Ana GarcíaCable HDMI 4KElectrónica599.9574.9575.0%completed
10132023-03-10Carlos LópezAlfombrilla XLAccesorios249.9833.9868.0%completed
10142023-03-15María MartínezLaptop Pro 15Electrónica11039.99259.9925.0%completed
10152023-03-20Juan RodríguezMonitor 27 4KElectrónica1474.99194.9941.1%completed
#ClientePaísSegmentoPedidosLifetime RevenueLifetime ProfitAvg OrderTier
5Laura GonzálezArgentinaGOLD52619.94963.94523.99VIP
1Ana GarcíaEspañaPREMIUM62142.87940.87428.57VIP
4Juan RodríguezEspañaSTANDARD52142.43772.43428.49VIP
8Miguel TorresColombiaPREMIUM51904.83890.83380.97High Value
3María MartínezMéxicoPREMIUM51799.91644.91359.98High Value
6Pedro SánchezEspañaSTANDARD51671.16742.16334.23High Value
7Sofía FernándezMéxicoGOLD51660.93769.93332.19High Value
10Roberto RuizChileGOLD41489.94695.94372.49High Value
2Carlos LópezEspañaSTANDARD51116.93546.93223.39High Value
9Carmen DíazEspañaSTANDARD5578.93285.93144.73Medium Value

Ingresos netos por mes (EUR)

Beneficio bruto por mes (EUR)

Ingresos por categoría de producto

Margen medio por categoría (%)

Ingresos netos por país

4Data Quality Tests

dbt ejecuta automáticamente tests de calidad sobre los modelos definidos en los schemas YAML. Todos los tests pasaron correctamente.

21
Tests totales
21
PASS
0
WARN
0
ERROR
unique_stg_orders_order_id
0.08s
not_null_stg_orders_order_id
0.06s
not_null_stg_orders_customer_id
0.05s
not_null_stg_orders_product_id
0.05s
accepted_values_stg_orders_status
0.07s
unique_stg_customers_customer_id
0.05s
not_null_stg_customers_customer_id
0.04s
unique_stg_customers_email
0.05s
not_null_stg_customers_email
0.04s
accepted_values_stg_customers_segment
0.06s
unique_stg_products_product_id
0.04s
not_null_stg_products_product_id
0.04s
unique_int_orders_enriched_order_id
0.05s
not_null_int_orders_enriched_order_id
0.04s
unique_fct_orders_order_id
0.05s
not_null_fct_orders_order_id
0.04s
not_null_fct_orders_net_revenue
0.05s
unique_dim_customers_customer_id
0.04s
not_null_dim_customers_customer_id
0.04s
not_null_dim_customers_value_tier
0.04s
accepted_values_dim_customers_value_tier
0.05s
PARA EMPRESAS

¿Tu equipo necesita llevar dbt a producción?

Este tutorial muestra el cómo. Si lo que necesitas es quien lo implemente — migración de Pentaho/SSIS/Informatica a dbt+Snowflake, modelado dimensional, CI/CD, integración con Power BI — te ponemos en contacto con consultores especializados.

✓ Consulta inicial gratuita · ✓ Sin compromiso · ✓ Respuesta rápida

Solicitar consultoría gratuita → Leer el tutorial completo