Featured image of post Build a Marketing Attribution Engine with DuckDB: From Multi-Channel Data to SaaS Monetization

Build a Marketing Attribution Engine with DuckDB: From Multi-Channel Data to SaaS Monetization

Master multi-touch marketing attribution with DuckDB using LATERAL JOIN, weighted models, and cohort analysis. Learn how to package this into a subscription SaaS product earning $5K+/month.

Build a Marketing Attribution Engine with DuckDB: From Multi-Channel Data to SaaS Monetization

The Problem: The “Black Box” of Marketing Data

Your company runs ads on Google, Facebook, TikTok, SEO, and email campaigns simultaneously. Every conversion comes from a combination of multiple touchpoints—but who gets the credit?

Traditional solutions fall into three categories:

  • Excel manual matching: 30+ minutes for 100K rows, and error-prone
  • Professional attribution tools (Northbeam, Triple Whale): $500-$2,000/month, and data must be exported to external platforms
  • Custom-built pipelines: Requires Spark + Airflow + dedicated engineers, 3+ months to deploy

DuckDB offers a completely different approach—doing on a single laptop what previously required a big data platform for attribution analysis.

Data Structure: Reconstructing the User Journey

The first step in attribution analysis is stitching scattered cross-channel user behaviors into a coherent journey. Assume you have the following raw data:

-- Create sample data (simulating multi-channel clicks and conversions)
CREATE TABLE ad_clicks AS
SELECT * FROM (VALUES
    ('U001', '2026-08-01 08:00:00', 'google',  'cpc', 12.50),
    ('U001', '2026-08-01 09:30:00', 'facebook', 'cpm',  0.00),
    ('U001', '2026-08-02 10:00:00', 'tiktok',  'cpv',  0.50),
    ('U002', '2026-08-01 11:00:00', 'google',  'cpc', 15.00),
    ('U002', '2026-08-03 14:00:00', 'seo',     'org',  0.00),
    ('U003', '2026-08-01 07:00:00', 'email',   'flat',0.00),
    ('U003', '2026-08-01 07:15:00', 'google',  'cpc',  8.00),
    ('U003', '2026-08-02 09:00:00', 'facebook', 'cpm',  0.00),
    ('U004', '2026-08-01 16:00:00', 'tiktok',  'cpv',  0.30),
    ('U004', '2026-08-02 12:00:00', 'google',  'cpc', 11.00),
    ('U005', '2026-08-01 20:00:00', 'seo',     'org',  0.00)
) AS t(user_id, timestamp, channel, cost_type, cost);

CREATE TABLE conversions AS
SELECT * FROM (VALUES
    ('U001', '2026-08-02 15:00:00', 299.00),
    ('U003', '2026-08-02 18:00:00', 149.00),
    ('U004', '2026-08-03 10:00:00', 499.00)
) AS t(user_id, convert_time, revenue);

Core Technique 1: LATERAL JOIN for Time-Windowed Journeys

The key to attribution analysis is—for each conversion, find all channels the user touched within a time window (e.g., 7 days). DuckDB’s LATERAL JOIN + UNNEST is the perfect tool:

-- Build complete user journey timeline for each conversion
WITH conversion_journey AS (
    SELECT 
        c.user_id,
        c.convert_time,
        c.revenue,
        -- Get all touchpoints within 7 days before conversion
        ARRAY_AGG(
            ROW(ac.channel, ac.timestamp, ac.cost)
            ORDER BY ac.timestamp ASC
        ) FILTER (
            WHERE ac.timestamp BETWEEN c.convert_time - INTERVAL '7' DAY 
                                   AND c.convert_time
        ) AS journey
    FROM conversions c
    LEFT JOIN ad_clicks ac ON ac.user_id = c.user_id
    GROUP BY c.user_id, c.convert_time, c.revenue
)
SELECT * FROM conversion_journey;

The result shows each user’s complete touchpoint sequence—the foundation for any attribution model.

Core Technique 2: Three Main Attribution Models

Last Touch Attribution

The simplest and most common—gives all credit to the last touchpoint.

-- Last Touch Attribution
SELECT 
    channel,
    COUNT(*) AS conversions,
    SUM(revenue) AS total_revenue,
    SUM(revenue) / COUNT(*) AS avg_order_value
FROM (
    SELECT 
        c.user_id,
        c.revenue,
        -- Take the most recent touchpoint in the journey
        (journey[ARRAY_LENGTH(journey)]).channel AS last_channel
    FROM conversion_journey c
) t
GROUP BY channel
ORDER BY total_revenue DESC;

First Touch Attribution

Credit goes to the channel that first introduced the user to the brand.

-- First Touch Attribution
SELECT 
    channel,
    COUNT(*) AS conversions,
    SUM(revenue) AS total_revenue
FROM (
    SELECT 
        c.user_id,
        c.revenue,
        (journey[1]).channel AS first_channel
    FROM conversion_journey c
) t
GROUP BY channel
ORDER BY total_revenue DESC;

Linear Attribution

Equal credit to every touchpoint—fair but conservative.

-- Linear Attribution: split revenue evenly across touchpoints
SELECT 
    jt.channel,
    COUNT(*) AS touchpoints,
    SUM(jt.revenue / jt.journey_size) AS attributed_revenue
FROM (
    SELECT 
        c.user_id,
        c.revenue,
        ARRAY_LENGTH(c.journey) AS journey_size,
        UNNEST(c.journey) WITH ORDINALITY AS jt(journey_item, position)
    FROM conversion_journey c
) t
LATERAL (SELECT (t.jt.journey_item).channel) AS channel_info
GROUP BY jt.channel
ORDER BY attributed_revenue DESC;

Time Decay Attribution (Most Advanced)

Touchpoints closer to the conversion get higher weight—this is the most sophisticated model.

-- Time Decay Attribution (exponential decay, half-life of 3 days)
SELECT 
    jt.channel,
    SUM(
        c.revenue * EXP(-0.231 * (c.convert_time - jt.ts)::DOUBLE)
    ) AS weighted_revenue
FROM conversion_journey c
LATERAL (
    SELECT 
        (item).channel AS channel,
        (item).timestamp AS ts,
        (item).cost AS cost
    FROM UNNEST(c.journey) AS item
) jt
GROUP BY jt.channel
ORDER BY weighted_revenue DESC;

Core Technique 3: Cohort Analysis for Long-Term Value

Attribution isn’t just about single conversions—it’s about understanding the long-term value of users acquired through different channels:

-- Cohort analysis by first-touch channel
WITH first_touch AS (
    SELECT 
        user_id,
        MIN(timestamp) AS first_touch_time,
        channel AS first_channel
    FROM ad_clicks
    GROUP BY user_id, channel
),
cohort_data AS (
    SELECT 
        ft.first_channel,
        DATE_TRUNC('week', ft.first_touch_time) AS cohort_week,
        COUNT(DISTINCT c.user_id) AS users,
        SUM(c.revenue) AS total_revenue,
        AVG(c.revenue) AS avg_revenue_per_user
    FROM first_touch ft
    JOIN conversions c ON c.user_id = ft.user_id
    GROUP BY ft.first_channel, DATE_TRUNC('week', ft.first_touch_time)
)
SELECT * FROM cohort_data
ORDER BY cohort_week, total_revenue DESC;

Performance Comparison: DuckDB vs Traditional Solutions

MetricExcel/VLOOKUPSpark + PythonDuckDB
1M row attribution query20+ min (often crashes)Needs cluster setup< 2 seconds
Memory usageGB-level (Excel limit)Several GB~50 MB
Deployment costFree$200+/month (cloud)Free
Learning curveSimple but error-proneComplexSQL-only
EmbeddabilityNoneRequires integration frameworkNative embedding

💡 Key Insight: DuckDB’s columnar storage + vectorized execution engine makes it 5-10x faster than Pandas for attribution analysis’s heavy aggregation and window functions, while reducing code volume by 60%.

Complete Runnable Code (Python + DuckDB)

import duckdb
import pandas as pd

# Connect to in-memory database (zero configuration)
con = duckdb.connect(":memory:")

# 1. Create sample data
con.execute("""
CREATE TABLE ad_clicks AS
SELECT * FROM read_csv_auto('ad_clicks.csv');

CREATE TABLE conversions AS
SELECT * FROM read_csv_auto('conversions.csv');
""")

# 2. Run attribution analysis
result = con.execute("""
WITH conversion_journey AS (
    SELECT 
        c.user_id,
        c.convert_time,
        c.revenue,
        ARRAY_AGG(
            ROW(ac.channel, ac.timestamp, ac.cost)
            ORDER BY ac.timestamp ASC
        ) FILTER (
            WHERE ac.timestamp BETWEEN c.convert_time - INTERVAL '7' DAY 
                                   AND c.convert_time
        ) AS journey
    FROM conversions c
    LEFT JOIN ad_clicks ac ON ac.user_id = c.user_id
    GROUP BY c.user_id, c.convert_time, c.revenue
)
SELECT 
    jt.channel,
    COUNT(*) AS touchpoints,
    SUM(c.revenue / ARRAY_LENGTH(c.journey)) AS linear_attribution
FROM conversion_journey c
LATERAL (SELECT (item).channel FROM UNNEST(c.journey) AS item) jt
GROUP BY jt.channel
ORDER BY linear_attribution DESC
""").fetchdf()

print(result)

From Analysis Tool to SaaS Product: Monetization Paths

This is the most critical part—turning this technology into revenue.

Wrap the DuckDB attribution engine into a web app for small e-commerce businesses and marketing agencies:

Product Architecture:
┌─────────────────────────────────────────┐
│  SaaS Platform (Streamlit / FastAPI)     │
│  ┌───────────┐  ┌───────────┐           │
│  │ Data Upload │  │ Attribution │           │
│  │ CSV/API    │  │ Model Select│           │
│  └─────┬─────┘  └─────┬─────┘           │
│        └───────┬───────┘                 │
│                ▼                         │
│      ┌─────────────────┐                │
│      │  DuckDB Engine   │                │
│      │  LATERAL JOIN    │                │
│      │  Window Functions│                │
│      └────────┬────────┘                │
│               ▼                         │
│      ┌─────────────────┐                │
│      │  Visual Reports  │                │
│      │  Export / Schedule│                │
│      └─────────────────┘                │
└─────────────────────────────────────────┘

Pricing Strategy:

  • Free tier: 10K records/month, 5 users
  • Professional: $49/month/business, unlimited records, API access
  • Enterprise: $199/month, multi-tenant + custom attribution models

Revenue Projection:

  • 50 paying customers × $49 = $2,450/month
  • 10 enterprise clients × $199 = $1,990/month
  • Total: ~$4,440/month

Path B: Project-Based Attribution Consulting

Offer one-time attribution analysis services for e-commerce businesses:

  • Single project fee: $1,000-$3,000
  • Maintenance: $300/month
  • 3-5 projects per month = $5,000+/month

Path C: Embedded Analytics API

Wrap the DuckDB attribution engine as a REST API:

  • Pay-per-call pricing: $0.005/call
  • 10,000 calls/day = $150/day = $4,500/month

Why DuckDB Instead of Other Solutions?

DimensionDuckDBSparkPython + PandasCommercial Tools
Deployment complexityZero dependenciesNeeds clusterModerateNone needed
Query performanceColumnar vectorizedDistributedMemory-limitedDepends on volume
Model flexibilityFully custom SQLProgrammable but complexProgrammable but slowFixed models
EmbeddabilityNative embeddingRequires REST serviceNeeds wrappingVia API
CostFree open-source$200+/monthFree$500+/month
Learning curveSQLPySparkPythonLow

Monetization Summary

  1. Fastest path: Build a free attribution analysis tool with Streamlit, promote through Google Ads/Facebook marketing communities, collect emails, then convert to paying users
  2. Differentiation: Commercial tools are expensive with fixed models; DuckDB enables custom attribution models (e.g., hybrid models combining business rules)
  3. Technical moat: Package attribution SQL as a DuckDB extension or Python package for reusable technical assets
  4. Expansion path: Attribution → LTV prediction → Budget allocation optimization, progressively building a complete marketing intelligence platform

📌 Call to Action: Run your first attribution analysis with DuckDB today. Prepare two CSV files (click records + conversion records), run the SQL above, and see how different attribution models change your channel budget recommendations—that’s your first sellable analysis report.


本文信息

项目内容
DuckDB 版本v1.5.x(部分功能基于 v2.0 Preview)
最后验证2026-09-12
测试环境Linux / x86_64 / 16GB RAM
官方文档DuckDB Documentation
GitHubpengzz9527/duckdb-blog

如发现错误,欢迎通过 GitHub Issue 或邮件 [email protected] 反馈。

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

Built with Hugo
Theme Stack designed by Jimmy