-- Staging → Warehouse transforms -- Executed in order by OlistLoader.run_warehouse_transform() -- All statements are idempotent via ON CONFLICT DO UPDATE -- ── 1. UPSERT dim_customers ──────────────────────────────────────────────── INSERT INTO warehouse.dim_customers ( customer_id, customer_unique_id, zip_code_prefix, city, state, lat, lng ) SELECT c.customer_id, c.customer_unique_id, c.customer_zip_code_prefix, INITCAP(c.customer_city) AS city, UPPER(c.customer_state) AS state, g.geolocation_lat::DOUBLE PRECISION AS lat, g.geolocation_lng::DOUBLE PRECISION AS lng FROM staging.stg_customers c LEFT JOIN staging.stg_geolocation g ON c.customer_zip_code_prefix = g.geolocation_zip_code_prefix ON CONFLICT (customer_id) DO UPDATE SET customer_unique_id = EXCLUDED.customer_unique_id, zip_code_prefix = EXCLUDED.zip_code_prefix, city = EXCLUDED.city, state = EXCLUDED.state, lat = EXCLUDED.lat, lng = EXCLUDED.lng, _updated_at = now(); -- ── 2. UPSERT dim_products ───────────────────────────────────────────────── INSERT INTO warehouse.dim_products ( product_id, category_name_pt, category_name_en, weight_g, length_cm, height_cm, width_cm ) SELECT p.product_id, p.product_category_name, t.product_category_name_english, p.product_weight_g::INTEGER, p.product_length_cm::INTEGER, p.product_height_cm::INTEGER, p.product_width_cm::INTEGER FROM staging.stg_products p LEFT JOIN staging.stg_product_category_translations t ON p.product_category_name = t.product_category_name ON CONFLICT (product_id) DO UPDATE SET category_name_pt = EXCLUDED.category_name_pt, category_name_en = EXCLUDED.category_name_en, weight_g = EXCLUDED.weight_g, length_cm = EXCLUDED.length_cm, height_cm = EXCLUDED.height_cm, width_cm = EXCLUDED.width_cm, _updated_at = now(); -- ── 3. UPSERT dim_sellers ────────────────────────────────────────────────── INSERT INTO warehouse.dim_sellers ( seller_id, zip_code_prefix, city, state, lat, lng ) SELECT s.seller_id, s.seller_zip_code_prefix, INITCAP(s.seller_city) AS city, UPPER(s.seller_state) AS state, g.geolocation_lat::DOUBLE PRECISION AS lat, g.geolocation_lng::DOUBLE PRECISION AS lng FROM staging.stg_sellers s LEFT JOIN staging.stg_geolocation g ON s.seller_zip_code_prefix = g.geolocation_zip_code_prefix ON CONFLICT (seller_id) DO UPDATE SET zip_code_prefix = EXCLUDED.zip_code_prefix, city = EXCLUDED.city, state = EXCLUDED.state, lat = EXCLUDED.lat, lng = EXCLUDED.lng, _updated_at = now(); -- ── 4. INSERT fact_orders (order-item grain) ──────────────────────────────── -- Aggregate one payment row per order (sum values, keep first payment_type) WITH order_payments AS ( SELECT order_id, SUM(payment_value::NUMERIC) AS payment_value, MIN(payment_type) AS payment_type, MAX(payment_installments::INTEGER) AS payment_installments FROM staging.stg_order_payments GROUP BY order_id ), order_reviews AS ( SELECT DISTINCT ON (order_id) order_id, review_score::SMALLINT AS review_score FROM staging.stg_order_reviews WHERE review_score ~ '^\d+$' ORDER BY order_id, review_answer_timestamp DESC ) INSERT INTO warehouse.fact_orders ( order_id, order_item_id, customer_key, product_key, seller_key, order_date_id, approved_date_id, delivered_date_id, estimated_delivery_id, order_status, price, freight_value, payment_value, payment_type, payment_installments, review_score, days_to_delivery, delivery_delay_days, is_late ) SELECT o.order_id, oi.order_item_id::SMALLINT, dc.customer_key, dp.product_key, ds.seller_key, -- Dates (cast timestamp text to date) DATE(o.order_purchase_timestamp::TIMESTAMP) AS order_date_id, CASE WHEN o.order_approved_at <> '' THEN DATE(o.order_approved_at::TIMESTAMP) END, CASE WHEN o.order_delivered_customer_date <> '' THEN DATE(o.order_delivered_customer_date::TIMESTAMP) END, CASE WHEN o.order_estimated_delivery_date <> '' THEN DATE(o.order_estimated_delivery_date::TIMESTAMP) END, o.order_status, oi.price::NUMERIC, oi.freight_value::NUMERIC, op.payment_value, op.payment_type, op.payment_installments::SMALLINT, orv.review_score, -- Derived delivery metrics CASE WHEN o.order_delivered_customer_date <> '' AND o.order_approved_at <> '' THEN DATE(o.order_delivered_customer_date::TIMESTAMP) - DATE(o.order_approved_at::TIMESTAMP) END AS days_to_delivery, CASE WHEN o.order_delivered_customer_date <> '' AND o.order_estimated_delivery_date <> '' THEN DATE(o.order_delivered_customer_date::TIMESTAMP) - DATE(o.order_estimated_delivery_date::TIMESTAMP) END AS delivery_delay_days, CASE WHEN o.order_delivered_customer_date <> '' AND o.order_estimated_delivery_date <> '' THEN DATE(o.order_delivered_customer_date::TIMESTAMP) > DATE(o.order_estimated_delivery_date::TIMESTAMP) ELSE NULL END AS is_late FROM staging.stg_orders o JOIN staging.stg_order_items oi USING (order_id) JOIN warehouse.dim_customers dc ON o.customer_id = dc.customer_id JOIN warehouse.dim_products dp ON oi.product_id = dp.product_id JOIN warehouse.dim_sellers ds ON oi.seller_id = ds.seller_id LEFT JOIN order_payments op ON o.order_id = op.order_id LEFT JOIN order_reviews orv ON o.order_id = orv.order_id ON CONFLICT (order_id, order_item_id) DO UPDATE SET order_status = EXCLUDED.order_status, price = EXCLUDED.price, freight_value = EXCLUDED.freight_value, payment_value = EXCLUDED.payment_value, payment_type = EXCLUDED.payment_type, payment_installments = EXCLUDED.payment_installments, review_score = EXCLUDED.review_score, days_to_delivery = EXCLUDED.days_to_delivery, delivery_delay_days = EXCLUDED.delivery_delay_days, is_late = EXCLUDED.is_late; -- ── 5. Update dim_customers aggregates from fact_orders ──────────────────── UPDATE warehouse.dim_customers dc SET first_order_date = agg.first_order_date, last_order_date = agg.last_order_date, total_orders = agg.total_orders, total_revenue = agg.total_revenue, _updated_at = now() FROM ( SELECT customer_key, MIN(order_date_id) AS first_order_date, MAX(order_date_id) AS last_order_date, COUNT(DISTINCT order_id) AS total_orders, SUM(price + freight_value) AS total_revenue FROM warehouse.fact_orders GROUP BY customer_key ) agg WHERE dc.customer_key = agg.customer_key; -- ── 6. Update dim_sellers aggregates from fact_orders ────────────────────── UPDATE warehouse.dim_sellers ds SET total_orders = agg.total_orders, avg_review_score = agg.avg_review_score, on_time_rate = agg.on_time_rate, _updated_at = now() FROM ( SELECT seller_key, COUNT(DISTINCT order_id) AS total_orders, AVG(review_score) AS avg_review_score, AVG(CASE WHEN is_late = false THEN 1.0 WHEN is_late = true THEN 0.0 ELSE NULL END) AS on_time_rate FROM warehouse.fact_orders GROUP BY seller_key ) agg WHERE ds.seller_key = agg.seller_key