All Industries

Retail & E-Commerce

Data solutions for omnichannel retail. From unified customer views to demand sensing and store operations, powered by AI agents for continuous optimization.

Customer 360Inventory AnalyticsPersonalizationStore Operations
All solutions powered by Claude Agents for Data Management
Learn About Our Agent Framework
Use Cases

Data Solutions for Retail

Technical approaches with architecture patterns and code examples.

Customer 360 & Unified Commerce

Single View of the Customer Across All Channels

Create a unified customer profile that spans online, in-store, mobile app, and loyalty program interactions for true omnichannel personalization.

The Challenge

Retailers collect customer data across dozens of touchpoints - POS systems, e-commerce platforms, mobile apps, loyalty programs, and customer service. Without unification, personalization efforts fail and marketing spend is wasted on duplicate or conflicting messages.

  • Customer identity fragmented across 20+ systems
  • No single view of purchase history across channels
  • Duplicate marketing to same customer via different IDs
  • Loyalty program data disconnected from transactions

Agent-Powered Solution

Deploy Claude Agents to discover, profile, and unify customer data across all touchpoints, creating a continuously updated golden customer record.

Data Discovery
Catalogue all customer data sources across channels
DQ Profiler
Profile data quality and identify matching candidates
Data Modeller
Design unified customer schema with identity resolution
Governance Checker
Ensure GDPR/CCPA compliance for customer data

Architecture


┌─────────────────────────────────────────────────────────────────┐
│                    RETAIL CUSTOMER 360 PLATFORM                  │
└─────────────────────────────────────────────────────────────────┘

Source Systems:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│  POS    │ │E-Commerce│ │ Mobile  │ │ Loyalty │ │Customer │
│ Systems │ │Platform  │ │   App   │ │ Program │ │ Service │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
     │           │           │           │           │
     └───────────┴───────────┴───────────┴───────────┘
                             │
              ┌──────────────▼──────────────┐
              │     Discovery Agent         │
              │  • Customer touchpoints     │
              │  • Transaction sources      │
              │  • Behavioral signals       │
              └──────────────┬──────────────┘
                             │
              ┌──────────────▼──────────────┐
              │     Identity Resolution     │
              │  • Email matching           │
              │  • Phone matching           │
              │  • Device fingerprinting    │
              │  • Address standardization  │
              └──────────────┬──────────────┘
                             │
              ┌──────────────▼──────────────┐
              │     Golden Customer Record   │
              │  ┌────────────────────────┐  │
              │  │ customer_id            │  │
              │  │ master_email           │  │
              │  │ lifetime_value         │  │
              │  │ preferred_channel      │  │
              │  │ segment                │  │
              │  │ churn_risk_score       │  │
              │  └────────────────────────┘  │
              └──────────────┬──────────────┘
                             │
         ┌───────────────────┼───────────────────┐
         ▼                   ▼                   ▼
   ┌───────────┐      ┌───────────┐      ┌───────────┐
   │Marketing  │      │ In-Store  │      │ Customer  │
   │Automation │      │ Clienteling│     │ Service   │
   └───────────┘      └───────────┘      └───────────┘

Implementation Example

-- Retail Customer 360 with Identity Resolution
-- models/gold/dim_customer_360.sql

{{
  config(
    materialized='incremental',
    unique_key='master_customer_id',
    tags=['customer_360', 'retail']
  )
}}

WITH identity_graph AS (
  -- Build identity clusters from all touchpoints
  SELECT
    COALESCE(
      e.customer_id,
      p.customer_id,
      m.customer_id,
      l.member_id
    ) as source_id,
    LOWER(TRIM(COALESCE(e.email, p.email, m.email, l.email))) as email,
    REGEXP_REPLACE(COALESCE(e.phone, p.phone, m.phone, l.phone), '[^0-9]', '') as phone_clean,
    -- Cluster on email first, then phone
    FIRST_VALUE(source_id) OVER (
      PARTITION BY email
      ORDER BY
        CASE
          WHEN e.customer_id IS NOT NULL THEN 1  -- E-commerce is primary
          WHEN l.member_id IS NOT NULL THEN 2    -- Loyalty second
          ELSE 3
        END
    ) as master_customer_id
  FROM {{ ref('silver_ecommerce_customers') }} e
  FULL OUTER JOIN {{ ref('silver_pos_customers') }} p ON e.email = p.email
  FULL OUTER JOIN {{ ref('silver_mobile_users') }} m ON e.email = m.email
  FULL OUTER JOIN {{ ref('silver_loyalty_members') }} l ON e.email = l.email
),

customer_metrics AS (
  SELECT
    ig.master_customer_id,
    -- Lifetime Value
    SUM(t.transaction_total) as lifetime_value,
    COUNT(DISTINCT t.transaction_id) as total_transactions,
    -- Channel Preferences
    MODE(t.channel) as preferred_channel,
    COUNT(DISTINCT t.channel) as channels_used,
    -- Recency
    MAX(t.transaction_date) as last_purchase_date,
    DATEDIFF(day, MAX(t.transaction_date), CURRENT_DATE()) as days_since_purchase,
    -- Frequency
    COUNT(DISTINCT DATE_TRUNC('month', t.transaction_date)) as active_months,
    -- Category Affinity
    ARRAY_AGG(DISTINCT t.category) as category_affinities
  FROM identity_graph ig
  JOIN {{ ref('silver_transactions') }} t ON ig.source_id = t.customer_id
  GROUP BY ig.master_customer_id
)

SELECT
  cm.master_customer_id,
  ig.email as master_email,
  ig.phone_clean as master_phone,
  cm.lifetime_value,
  cm.total_transactions,
  cm.preferred_channel,
  cm.channels_used,
  cm.last_purchase_date,
  cm.days_since_purchase,
  -- Segmentation
  CASE
    WHEN cm.lifetime_value > 5000 AND cm.days_since_purchase < 30 THEN 'VIP_ACTIVE'
    WHEN cm.lifetime_value > 5000 AND cm.days_since_purchase < 90 THEN 'VIP_AT_RISK'
    WHEN cm.lifetime_value > 1000 AND cm.days_since_purchase < 60 THEN 'LOYAL'
    WHEN cm.days_since_purchase < 30 THEN 'ACTIVE'
    WHEN cm.days_since_purchase < 180 THEN 'LAPSING'
    ELSE 'CHURNED'
  END as customer_segment,
  -- Churn Risk Score (0-100)
  LEAST(100, cm.days_since_purchase * 0.5 + (90 - cm.active_months) * 2) as churn_risk_score,
  cm.category_affinities,
  CURRENT_TIMESTAMP() as _updated_at
FROM customer_metrics cm
JOIN identity_graph ig ON cm.master_customer_id = ig.master_customer_id

Expected Outcomes

90% customer identity match rate across channels
35% improvement in marketing campaign ROI
50% reduction in duplicate customer outreach
Real-time customer view for in-store clienteling

Inventory Analytics & Demand Sensing

Optimize Stock Levels and Reduce Waste

Use AI-powered demand forecasting and real-time inventory visibility to reduce stockouts, minimize overstock, and optimize fulfillment.

The Challenge

Retailers lose billions annually to stockouts and overstock situations. Traditional forecasting methods cannot account for external signals like weather, events, and social trends that drive demand.

  • Disconnected inventory data across warehouses and stores
  • Inaccurate demand forecasts leading to stockouts
  • Excess inventory tying up working capital
  • No visibility into real-time inventory positions

Agent-Powered Solution

Implement Claude Agents to profile inventory data quality, build unified inventory views, and enable accurate demand sensing across the network.

Data Discovery
Map all inventory sources and data flows
DQ Profiler
Validate inventory accuracy and identify discrepancies
Data Modeller
Design inventory mart with demand features

Architecture


┌─────────────────────────────────────────────────────────────────┐
│                  INVENTORY & DEMAND PLATFORM                     │
└─────────────────────────────────────────────────────────────────┘

Data Sources:
┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ Warehouse   │  │   Store     │  │  Supplier   │  │  External   │
│ Management  │  │    POS      │  │   Portal    │  │  Signals    │
│   (WMS)     │  │             │  │             │  │(Weather/Cal)│
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │                │
       └────────────────┼────────────────┼────────────────┘
                        │
            ┌───────────▼───────────┐
            │   Bronze Layer        │
            │   • Raw inventory     │
            │   • POS transactions  │
            │   • Supplier data     │
            └───────────┬───────────┘
                        │
            ┌───────────▼───────────┐
            │   DQ Profiler Agent   │
            │   • Stock accuracy    │
            │   • Location validity │
            │   • SKU consistency   │
            └───────────┬───────────┘
                        │
            ┌───────────▼───────────┐
            │   Silver Layer        │
            │   • Unified inventory │
            │   • Sales velocity    │
            │   • Lead times        │
            └───────────┬───────────┘
                        │
            ┌───────────▼───────────┐
            │   Gold Layer          │
            │   ┌─────────────────┐ │
            │   │ fact_inventory  │ │
            │   │ • stock_on_hand │ │
            │   │ • days_of_supply│ │
            │   │ • reorder_point │ │
            │   │ • demand_forecast│ │
            │   └─────────────────┘ │
            └───────────┬───────────┘
                        │
         ┌──────────────┼──────────────┐
         ▼              ▼              ▼
   ┌──────────┐  ┌──────────────┐  ┌──────────┐
   │ Replenish│  │   Demand     │  │  Store   │
   │ Planning │  │  Forecasting │  │ Ordering │
   └──────────┘  └──────────────┘  └──────────┘

Implementation Example

-- Inventory Position with Demand Sensing
-- models/gold/fact_inventory_position.sql

{{
  config(
    materialized='incremental',
    unique_key='location_sku_date_key',
    partition_by={'field': 'snapshot_date', 'data_type': 'date'},
    cluster_by=['location_id', 'category']
  )
}}

WITH current_inventory AS (
  SELECT
    location_id,
    sku,
    SUM(quantity_on_hand) as stock_on_hand,
    SUM(quantity_in_transit) as in_transit,
    SUM(quantity_reserved) as reserved
  FROM {{ ref('silver_inventory_positions') }}
  WHERE snapshot_date = CURRENT_DATE()
  GROUP BY location_id, sku
),

sales_velocity AS (
  SELECT
    location_id,
    sku,
    -- 7-day rolling average
    AVG(units_sold) OVER (
      PARTITION BY location_id, sku
      ORDER BY sale_date
      ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) as avg_daily_sales_7d,
    -- 28-day rolling average for trend
    AVG(units_sold) OVER (
      PARTITION BY location_id, sku
      ORDER BY sale_date
      ROWS BETWEEN 27 PRECEDING AND CURRENT ROW
    ) as avg_daily_sales_28d,
    -- Day of week patterns
    AVG(units_sold) OVER (
      PARTITION BY location_id, sku, DAYOFWEEK(sale_date)
      ORDER BY sale_date
      ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
    ) as dow_adjusted_demand
  FROM {{ ref('silver_daily_sales') }}
  QUALIFY sale_date = CURRENT_DATE() - 1
),

external_signals AS (
  SELECT
    location_id,
    signal_date,
    weather_impact_factor,
    event_impact_factor,
    promotion_impact_factor
  FROM {{ ref('silver_demand_signals') }}
  WHERE signal_date BETWEEN CURRENT_DATE() AND CURRENT_DATE() + 14
),

demand_forecast AS (
  SELECT
    sv.location_id,
    sv.sku,
    sv.avg_daily_sales_7d,
    -- Adjust forecast with external signals
    sv.avg_daily_sales_7d
      * COALESCE(es.weather_impact_factor, 1.0)
      * COALESCE(es.event_impact_factor, 1.0)
      * COALESCE(es.promotion_impact_factor, 1.0) as adjusted_daily_forecast,
    -- 14-day forward demand
    SUM(sv.avg_daily_sales_7d
      * COALESCE(es.weather_impact_factor, 1.0)
      * COALESCE(es.event_impact_factor, 1.0)) as forecast_14d
  FROM sales_velocity sv
  LEFT JOIN external_signals es ON sv.location_id = es.location_id
  GROUP BY 1, 2, 3, 4
)

SELECT
  {{ dbt_utils.generate_surrogate_key(['ci.location_id', 'ci.sku', 'CURRENT_DATE()']) }}
    as location_sku_date_key,
  CURRENT_DATE() as snapshot_date,
  ci.location_id,
  ci.sku,
  p.category,
  ci.stock_on_hand,
  ci.in_transit,
  ci.reserved,
  ci.stock_on_hand - ci.reserved as available_to_sell,
  df.avg_daily_sales_7d,
  df.adjusted_daily_forecast,
  -- Days of Supply
  CASE
    WHEN df.adjusted_daily_forecast > 0
    THEN ci.stock_on_hand / df.adjusted_daily_forecast
    ELSE 999
  END as days_of_supply,
  -- Stock Status
  CASE
    WHEN ci.stock_on_hand = 0 THEN 'OUT_OF_STOCK'
    WHEN ci.stock_on_hand / NULLIF(df.adjusted_daily_forecast, 0) < 7 THEN 'LOW_STOCK'
    WHEN ci.stock_on_hand / NULLIF(df.adjusted_daily_forecast, 0) > 60 THEN 'OVERSTOCK'
    ELSE 'HEALTHY'
  END as stock_status,
  -- Reorder Signal
  CASE
    WHEN ci.stock_on_hand + ci.in_transit < (df.adjusted_daily_forecast * p.lead_time_days * 1.5)
    THEN TRUE
    ELSE FALSE
  END as reorder_required,
  df.forecast_14d,
  CURRENT_TIMESTAMP() as _loaded_at
FROM current_inventory ci
JOIN {{ ref('dim_product') }} p ON ci.sku = p.sku
LEFT JOIN demand_forecast df ON ci.location_id = df.location_id AND ci.sku = df.sku

Expected Outcomes

25% reduction in stockout incidents
18% decrease in excess inventory
$2M annual savings in carrying costs
95% inventory accuracy across all locations

Personalization & Recommendations

AI-Driven Product Recommendations

Deliver personalized product recommendations, offers, and content across all channels based on unified customer understanding.

The Challenge

Generic recommendations lead to poor conversion rates. Without a unified view of customer preferences across channels, personalization efforts are fragmented and ineffective.

  • Recommendations based on incomplete purchase history
  • No real-time personalization for anonymous visitors
  • Siloed personalization across web, email, and stores
  • Cold start problem for new customers

Agent-Powered Solution

Build a unified feature store powered by Claude Agents that enables real-time personalization across all channels and customer lifecycle stages.

Data Discovery
Identify all behavioral and transactional signals
DQ Profiler
Ensure feature quality for ML models
Data Modeller
Design feature store schema for recommendations

Architecture


┌─────────────────────────────────────────────────────────────────┐
│                  PERSONALIZATION PLATFORM                        │
└─────────────────────────────────────────────────────────────────┘

Behavioral Signals:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│   Browse     │ │   Search     │ │   Purchase   │ │   Email      │
│   Clickstream│ │   Queries    │ │   History    │ │   Engagement │
└───────┬──────┘ └───────┬──────┘ └───────┬──────┘ └───────┬──────┘
        │                │                │                │
        └────────────────┴────────────────┴────────────────┘
                                │
                   ┌────────────▼────────────┐
                   │    Event Streaming      │
                   │    (Kafka/Kinesis)      │
                   └────────────┬────────────┘
                                │
               ┌────────────────┼────────────────┐
               ▼                                 ▼
   ┌───────────────────┐              ┌───────────────────┐
   │  Batch Features   │              │  Real-time        │
   │  (Daily refresh)  │              │  Features         │
   │  • LTV score      │              │  • Session intent │
   │  • Category affinity│            │  • Cart context   │
   │  • Segment        │              │  • Recent views   │
   └─────────┬─────────┘              └─────────┬─────────┘
             │                                  │
             └──────────────┬───────────────────┘
                            ▼
               ┌────────────────────────┐
               │     Feature Store      │
               │  ┌──────────────────┐  │
               │  │ customer_features│  │
               │  │ product_features │  │
               │  │ context_features │  │
               │  └──────────────────┘  │
               └────────────┬───────────┘
                            │
         ┌──────────────────┼──────────────────┐
         ▼                  ▼                  ▼
   ┌───────────┐     ┌───────────┐     ┌───────────┐
   │   Web     │     │   Email   │     │   Store   │
   │   Recs    │     │   Content │     │  Clientel │
   └───────────┘     └───────────┘     └───────────┘

Implementation Example

-- Customer Feature Store for Personalization
-- models/features/customer_features.sql

{{
  config(
    materialized='incremental',
    unique_key='customer_id',
    tags=['features', 'personalization']
  )
}}

WITH browse_behavior AS (
  SELECT
    customer_id,
    -- Category browsing patterns (last 30 days)
    OBJECT_AGG(
      category,
      TO_VARIANT(OBJECT_CONSTRUCT(
        'views', view_count,
        'time_spent_seconds', total_time,
        'recency_days', days_since_last_view
      ))
    ) as category_browse_scores,
    -- Search intent signals
    ARRAY_AGG(DISTINCT search_term) FILTER (WHERE search_date > CURRENT_DATE() - 7)
      as recent_searches
  FROM {{ ref('silver_browse_sessions') }}
  WHERE session_date > CURRENT_DATE() - 30
  GROUP BY customer_id
),

purchase_patterns AS (
  SELECT
    customer_id,
    -- Category purchase affinity (all time, weighted by recency)
    OBJECT_AGG(
      category,
      TO_VARIANT(OBJECT_CONSTRUCT(
        'purchase_count', purchase_count,
        'total_spent', total_spent,
        'affinity_score', affinity_score
      ))
    ) as category_purchase_affinity,
    -- Price sensitivity
    AVG(CASE WHEN is_discounted THEN 1 ELSE 0 END) as discount_sensitivity,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY item_price) as median_price_point,
    -- Timing patterns
    MODE(DAYOFWEEK(purchase_date)) as preferred_day,
    MODE(HOUR(purchase_timestamp)) as preferred_hour
  FROM {{ ref('silver_transactions') }}
  GROUP BY customer_id
),

engagement_scores AS (
  SELECT
    customer_id,
    -- Email engagement (for content optimization)
    AVG(CASE WHEN opened THEN 1 ELSE 0 END) as email_open_rate,
    AVG(CASE WHEN clicked THEN 1 ELSE 0 END) as email_click_rate,
    -- Content preferences
    ARRAY_AGG(DISTINCT content_type) FILTER (WHERE clicked) as preferred_content_types
  FROM {{ ref('silver_email_engagement') }}
  WHERE sent_date > CURRENT_DATE() - 90
  GROUP BY customer_id
),

product_recommendations AS (
  SELECT
    customer_id,
    -- Collaborative filtering results (pre-computed)
    cf.recommended_skus,
    cf.recommendation_scores,
    -- Recently viewed (for "continue shopping")
    rv.recently_viewed_skus,
    -- Abandoned cart items
    ac.abandoned_skus
  FROM {{ ref('ml_collaborative_filtering') }} cf
  LEFT JOIN {{ ref('silver_recent_views') }} rv USING (customer_id)
  LEFT JOIN {{ ref('silver_abandoned_carts') }} ac USING (customer_id)
)

SELECT
  c.customer_id,
  -- Identity & Segment
  c.customer_segment,
  c.lifetime_value,
  c.churn_risk_score,
  -- Browse Features
  bb.category_browse_scores,
  bb.recent_searches,
  -- Purchase Features
  pp.category_purchase_affinity,
  pp.discount_sensitivity,
  pp.median_price_point,
  pp.preferred_day,
  pp.preferred_hour,
  -- Engagement Features
  es.email_open_rate,
  es.email_click_rate,
  es.preferred_content_types,
  -- Recommendations
  pr.recommended_skus,
  pr.recommendation_scores,
  pr.recently_viewed_skus,
  pr.abandoned_skus,
  -- Feature freshness
  CURRENT_TIMESTAMP() as features_updated_at
FROM {{ ref('dim_customer_360') }} c
LEFT JOIN browse_behavior bb ON c.customer_id = bb.customer_id
LEFT JOIN purchase_patterns pp ON c.customer_id = pp.customer_id
LEFT JOIN engagement_scores es ON c.customer_id = es.customer_id
LEFT JOIN product_recommendations pr ON c.customer_id = pr.customer_id

Expected Outcomes

40% increase in recommendation click-through rate
28% improvement in email conversion
3x improvement in new customer personalization
Unified personalization across all channels

Store Operations Analytics

Optimize Labor, Merchandising & Performance

Data-driven insights for store operations including labor optimization, planogram compliance, and performance benchmarking.

The Challenge

Store managers lack real-time insights into operational efficiency. Labor scheduling is based on gut feel, and merchandising compliance cannot be monitored at scale.

  • No visibility into real-time store traffic patterns
  • Suboptimal labor scheduling leading to over/understaffing
  • Manual planogram compliance checks are inconsistent
  • Siloed store performance data prevents benchmarking

Agent-Powered Solution

Build an operational data platform that unifies traffic, labor, sales, and compliance data for real-time store optimization.

Data Discovery
Map all store operational data sources
DQ Profiler
Validate traffic and labor data accuracy
Data Modeller
Design store operations data mart
Governance Checker
Ensure employee data privacy compliance

Architecture


┌─────────────────────────────────────────────────────────────────┐
│                  STORE OPERATIONS PLATFORM                       │
└─────────────────────────────────────────────────────────────────┘

Data Sources:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│   Traffic    │ │   Labor      │ │    POS       │ │  Compliance  │
│   Counters   │ │   System     │ │   Sales      │ │   Scans      │
└───────┬──────┘ └───────┬──────┘ └───────┬──────┘ └───────┬──────┘
        │                │                │                │
        └────────────────┴────────────────┴────────────────┘
                                │
                   ┌────────────▼────────────┐
                   │    Hourly Aggregation   │
                   │    (Bronze → Silver)    │
                   └────────────┬────────────┘
                                │
                   ┌────────────▼────────────┐
                   │   Store Metrics Layer   │
                   │   ┌──────────────────┐  │
                   │   │ Conversion Rate  │  │
                   │   │ Sales per Hour   │  │
                   │   │ Labor Efficiency │  │
                   │   │ Basket Size      │  │
                   │   └──────────────────┘  │
                   └────────────┬────────────┘
                                │
         ┌──────────────────────┼──────────────────────┐
         ▼                      ▼                      ▼
   ┌───────────┐         ┌───────────┐         ┌───────────┐
   │   Labor   │         │   Store   │         │  District │
   │ Scheduling│         │ Dashboards│         │ Comparison│
   └───────────┘         └───────────┘         └───────────┘

Implementation Example

-- Store Operations Metrics
-- models/gold/fact_store_hourly_performance.sql

{{
  config(
    materialized='incremental',
    unique_key='store_hour_key',
    partition_by={'field': 'business_date', 'data_type': 'date'}
  )
}}

WITH traffic AS (
  SELECT
    store_id,
    business_date,
    hour_of_day,
    SUM(entries) as hourly_traffic,
    AVG(dwell_time_seconds) as avg_dwell_time
  FROM {{ ref('silver_traffic_counts') }}
  WHERE business_date >= CURRENT_DATE() - 90
  GROUP BY 1, 2, 3
),

sales AS (
  SELECT
    store_id,
    DATE(transaction_timestamp) as business_date,
    HOUR(transaction_timestamp) as hour_of_day,
    COUNT(DISTINCT transaction_id) as transactions,
    SUM(transaction_total) as hourly_sales,
    SUM(item_count) as units_sold,
    COUNT(DISTINCT customer_id) as unique_customers
  FROM {{ ref('silver_transactions') }}
  WHERE DATE(transaction_timestamp) >= CURRENT_DATE() - 90
  GROUP BY 1, 2, 3
),

labor AS (
  SELECT
    store_id,
    work_date as business_date,
    hour_of_day,
    SUM(scheduled_hours) as scheduled_labor_hours,
    SUM(actual_hours) as actual_labor_hours,
    COUNT(DISTINCT employee_id) as staff_count
  FROM {{ ref('silver_labor_hours') }}
  WHERE work_date >= CURRENT_DATE() - 90
  GROUP BY 1, 2, 3
)

SELECT
  {{ dbt_utils.generate_surrogate_key(['t.store_id', 't.business_date', 't.hour_of_day']) }}
    as store_hour_key,
  t.store_id,
  t.business_date,
  t.hour_of_day,
  s.store_name,
  s.region,
  s.district,
  -- Traffic Metrics
  t.hourly_traffic,
  t.avg_dwell_time,
  -- Sales Metrics
  COALESCE(sl.transactions, 0) as transactions,
  COALESCE(sl.hourly_sales, 0) as hourly_sales,
  COALESCE(sl.units_sold, 0) as units_sold,
  -- Conversion
  CASE
    WHEN t.hourly_traffic > 0
    THEN sl.transactions::FLOAT / t.hourly_traffic
    ELSE 0
  END as conversion_rate,
  -- Basket Metrics
  CASE
    WHEN sl.transactions > 0
    THEN sl.hourly_sales / sl.transactions
    ELSE 0
  END as average_transaction_value,
  CASE
    WHEN sl.transactions > 0
    THEN sl.units_sold::FLOAT / sl.transactions
    ELSE 0
  END as average_basket_size,
  -- Labor Metrics
  COALESCE(l.scheduled_labor_hours, 0) as scheduled_hours,
  COALESCE(l.actual_labor_hours, 0) as actual_hours,
  COALESCE(l.staff_count, 0) as staff_count,
  -- Labor Efficiency
  CASE
    WHEN l.actual_labor_hours > 0
    THEN sl.hourly_sales / l.actual_labor_hours
    ELSE 0
  END as sales_per_labor_hour,
  CASE
    WHEN l.actual_labor_hours > 0
    THEN sl.transactions::FLOAT / l.actual_labor_hours
    ELSE 0
  END as transactions_per_labor_hour,
  -- Optimal Staffing Indicator
  CASE
    WHEN t.hourly_traffic / NULLIF(l.staff_count, 0) > 50 THEN 'UNDERSTAFFED'
    WHEN t.hourly_traffic / NULLIF(l.staff_count, 0) < 15 THEN 'OVERSTAFFED'
    ELSE 'OPTIMAL'
  END as staffing_status,
  CURRENT_TIMESTAMP() as _loaded_at
FROM traffic t
JOIN {{ ref('dim_store') }} s ON t.store_id = s.store_id
LEFT JOIN sales sl ON t.store_id = sl.store_id
  AND t.business_date = sl.business_date
  AND t.hour_of_day = sl.hour_of_day
LEFT JOIN labor l ON t.store_id = l.store_id
  AND t.business_date = l.business_date
  AND t.hour_of_day = l.hour_of_day

Expected Outcomes

12% improvement in labor cost efficiency
8% increase in conversion rate through better staffing
Real-time visibility into all 500+ stores
Automated anomaly detection for operational issues
Success Stories

Retail in Action

Anonymised examples of how we've helped retailers transform their data operations.

Fashion Retailer • Customer 360

Unified Customer Data Platform

Built a customer 360 platform unifying online, in-store, and loyalty data for 2M+ customers, enabling personalised marketing at scale.

25% increase in email campaign conversion
30% improvement in customer lifetime value
SnowflakeSegmentdbt
Grocery Chain • Inventory

Real-Time Inventory Optimisation

Implemented real-time inventory analytics across 150+ stores, optimising stock levels and reducing waste in fresh categories.

20% reduction in out-of-stock events
15% reduction in fresh produce waste
DatabricksKafkaPower BI

Client names withheld by policy. References available under NDA.

Ready to Transform Your Retail Data?

Book a discovery call to discuss your omnichannel data challenges.