SQL • SQL FOR ANALYTICS AND REPORTING

Defining & Implementing Metrics — Define metrics precisely and implement them in SQL

Transform ambiguous business questions into rigorous, reproducible SQL metric definitions that drive reliable analytics.

Historical Context & Motivation

In the earliest days of commercial databases, organizations stored transactional records and rarely asked questions beyond simple counts and sums. As relational databases matured through the 1980s and 1990s, the emergence of data warehousing introduced the idea that data could serve a strategic purpose — informing decisions through carefully defined business metrics and key performance indicators (KPIs). Yet the gap between what a business stakeholder meant by a metric like "monthly active users" and the SQL query an analyst actually wrote proved to be one of the most persistent and costly sources of error in analytics engineering.

The discipline of precisely defining metrics before implementing them emerged as organizations discovered that different teams would produce wildly different numbers for the same supposedly identical question. A marketing team might count "active users" as anyone who logged in, while a product team might require a meaningful interaction such as completing a purchase or viewing three pages. Without a formal metric specification — including the entity being measured, the qualifying event, the time grain, and the aggregation function — these discrepancies would persist and erode trust in the data organization.

1993
The Data Warehouse Toolkit
Ralph Kimball publishes foundational work on dimensional modeling, introducing the concepts of facts and dimensions that underpin metric computation to this day.
2005
Rise of Web Analytics
Google Analytics and similar platforms popularize metrics like page views, bounce rate, and sessions, exposing millions of non-technical users to the importance of precise metric definitions.
2012
Lean Analytics Movement
Alistair Croll and Ben Yoskovitz argue that startups should identify a single "One Metric That Matters" at each stage, emphasizing clarity and rigor in metric selection.
2020
Metrics Layer & Semantic Models
Tools like dbt metrics, Looker's semantic layer, and MetricFlow emerge to codify metric definitions in version-controlled configuration files, decoupling the definition of a metric from its implementation.
2023
Analytics Engineering as a Discipline
The analytics engineering role is firmly established, with metric definition, testing, and documentation recognized as core competencies alongside SQL fluency.

The central question this lesson addresses is deceptively simple: how do you take a vague business question and translate it into a deterministic, reproducible SQL query? Answering it requires a structured process that bridges the gap between stakeholder intent and database semantics, and the consequences of doing it poorly — misallocated budgets, incorrect A/B test conclusions, regulatory reporting errors — can be severe.

Core Principles of Metric Definition

A well-defined metric is not merely a SQL query; it is a contract between the data team and the business. Before writing any code, every metric must be decomposed into a set of unambiguous components. These components collectively eliminate the interpretive ambiguity that causes two analysts working independently to produce different numbers for the same question. The following principles form the foundation of rigorous metric design.

1

Entity

Identify the grain — what unit is being counted or measured? Users, orders, sessions, or products? The entity determines the GROUP BY and COUNT(DISTINCT ...) target.
2

Event & Filter

Define the qualifying action and any inclusion/exclusion criteria. A "purchase" might exclude refunds, test accounts, or internal orders. This maps directly to WHERE and HAVING clauses.
3

Time Grain

Specify the temporal window: daily, weekly (ISO or Sunday-start?), monthly, trailing-28-day, or calendar quarter. Time grain affects both the DATE_TRUNC logic and join conditions.
4

Aggregation Function

Choose COUNT, SUM, AVG, PERCENTILE_CONT, or a ratio. Distinguish additive metrics (summable across dimensions) from non-additive ones (ratios, distinct counts).
5

Semantic Ownership

Assign a single owner and document the metric in a data catalog or semantic layer. Include the business definition, the SQL implementation, known caveats, and a freshness SLA. This prevents definition drift over time.
KEY TAKEAWAY
Think of a metric definition as a recipe in a cookbook. The recipe (metric specification) is distinct from the act of cooking (writing SQL). If two chefs follow the same recipe — with the same ingredients (entity), preparation steps (filters), cooking time (time grain), and plating style (aggregation) — they produce the same dish. Without a written recipe, each chef improvises, and the results diverge. A metric spec ensures every analyst produces the same number from the same data.

From Business Question to SQL Query

The following diagram illustrates the end-to-end workflow for translating a stakeholder's question into a production-ready SQL metric. The process flows from left to right through four stages: the raw business question is disambiguated into a formal metric specification, which is then mapped onto specific database tables and columns, and finally encoded as a deterministic SQL query. Notice that the specification stage is where most ambiguity is resolved — it acts as the critical bridge between human intent and machine execution.

The pipeline progresses from a vague business question (Stage 1) through a formal metric specification (Stage 2), a mapping to physical database columns (Stage 3), and finally a deterministic SQL query (Stage 4). The bottom checklist highlights the four questions — Who, What, When, and How — that must be answered to eliminate ambiguity.

The diagram emphasizes that the specification stage (Stage 2) is the most critical part of the pipeline. Many analysts jump directly from a business question to SQL, skipping the formal specification entirely. This shortcut introduces subtle bugs that are difficult to detect: a missing filter, an incorrect time boundary, or a COUNT where a COUNT(DISTINCT) was needed. By forcing yourself to answer the four checklist questions explicitly — entity, event, time grain, and aggregation — you create a testable artifact that can be reviewed by peers and stakeholders before any SQL is written.

Formal Metric Structure & SQL Mapping

Every metric can be expressed as a function applied to a filtered, grouped dataset. Understanding this formal structure helps you systematically translate any metric definition into SQL. We can formalize a metric M as a tuple of components that map directly to SQL clauses.

METRIC FORMAL DEFINITION
M = ⟨ E, F, G, A ⟩
Where E = entity (the unit of analysis, e.g., user_id), F = filter predicate (the WHERE clause conditions), G = grouping dimensions (time grain and any segmentation columns), A = aggregation function (COUNT, SUM, AVG, ratios, etc.).

This tuple maps onto a SQL query template that applies to virtually all analytical metrics. The following template shows exactly how each component in the formal definition corresponds to a SQL clause.

SQL TEMPLATE MAPPING
SELECT G, A(E) FROM source_table WHERE F GROUP BY G
The SELECT clause contains the grouping dimensions G and the aggregation A applied to entity E. The WHERE clause encodes filter F. For ratio metrics, A becomes a composite expression like SUM(CASE WHEN ... THEN 1 ELSE 0 END) / COUNT(*).

Additive vs. Non-Additive Metrics

A critical distinction in metric design is whether a metric is additive, meaning its values can be summed across any dimension without loss of correctness. Revenue is additive: daily revenue sums to monthly revenue. However, non-additive metrics such as COUNT(DISTINCT user_id) or conversion rate cannot be summed. If 500 distinct users were active on Monday and 600 on Tuesday, the two-day total is not 1,100 — some users may appear on both days. This distinction directly affects how you structure CTEs and roll-ups in SQL.

CONVERSION RATE (RATIO METRIC)
Conversion Rate = COUNT(DISTINCT users with purchase) / COUNT(DISTINCT users with session) × 100
Both numerator and denominator use COUNT(DISTINCT), making this metric doubly non-additive. You must compute it from raw events at the desired grain — never by averaging pre-computed daily rates.

Taxonomy of Common Metric Patterns

Metrics in practice fall into a small number of recurring patterns. Recognizing these patterns allows you to select the correct SQL template quickly and avoid common pitfalls. The following diagram classifies the most common metric types and shows their SQL signatures, additivity properties, and typical use cases.

The taxonomy classifies metrics into three families: simple aggregates (direct applications of COUNT, SUM, or AVG), ratio/rate metrics (always non-additive), and window metrics that use SQL window functions. The reference table at the bottom maps each metric component to its corresponding SQL clause.
Common metric types, their SQL signatures, additivity properties, and frequent implementation mistakes.
Metric TypeSQL SignatureAdditive?Common Pitfall
Event CountCOUNT(*)✓ YesCounting duplicates when events are not deduplicated
Distinct Entity CountCOUNT(DISTINCT entity_id)✗ NoSumming daily distinct counts to get monthly — yields over-count
Total AmountSUM(amount)✓ YesNot handling NULLs — SUM ignores them silently
Conversion RateCOUNT(DISTINCT converted) / COUNT(DISTINCT all)✗ NoAveraging pre-computed rates instead of recomputing from raw data
Rolling AverageAVG(val) OVER (ORDER BY date ROWS 6 PRECEDING)✗ NoConfusing ROWS with RANGE — RANGE includes ties, changing the window size

Worked Example: Monthly Active Users (MAU)

Let us walk through the complete process of defining and implementing a classic metric: Monthly Active Users (MAU). We will start with an ambiguous stakeholder request, formalize a metric specification, and then write production-quality SQL. Assume we have an events table with columns event_id, user_id, event_type, and created_at (timestamp), and a users table with user_id, is_test_account (boolean), and country_code.

Defining & Implementing MAU
1
Step 1 — Capture the Business QuestionThe VP of Product asks: "How many active users did we have each month last quarter?" This question is ambiguous on at least three axes: the definition of "active," whether test accounts should be included, and whether the month boundaries are calendar months or rolling 30-day windows.
2
Step 2 — Write the Metric SpecificationAfter a clarification conversation, you document the following spec. Entity: user_id. Qualifying Event: any event_type in ('login', 'page_view', 'purchase'). Time Grain: calendar month (UTC). Aggregation: COUNT(DISTINCT user_id). Exclusions: users where is_test_account = TRUE. Date Range: 2024-07-01 to 2024-09-30.
Spec documented: M = ⟨ user_id, event_type IN (...) ∧ ¬is_test, calendar_month, COUNT(DISTINCT) ⟩
3
Step 3 — Map to Physical TablesThe entity user_id lives in both events and users. The qualifying events come from events.event_type. The test-account filter requires a JOIN to users. The time grain uses DATE_TRUNC('month', events.created_at).
4
Step 4 — Write the SQL QueryTranslating the spec into SQL: SELECT DATE_TRUNC('month', e.created_at) AS metric_month, COUNT(DISTINCT e.user_id) AS mau FROM events AS e INNER JOIN users AS u ON e.user_id = u.user_id WHERE e.event_type IN ('login', 'page_view', 'purchase') AND u.is_test_account = FALSE AND e.created_at >= '2024-07-01' AND e.created_at < '2024-10-01' GROUP BY 1 ORDER BY 1; Note the use of < '2024-10-01' rather than <= '2024-09-30' to correctly handle timestamps that include a time component past midnight on September 30.
Query returns one row per month with the distinct count of non-test users who performed a qualifying event.
5
Step 5 — Validate & DocumentRun sanity checks: compare the total against a known data quality dashboard; spot-check a sample month by listing individual user_ids; verify that no test accounts appear in the output using SELECT DISTINCT u.is_test_account FROM events e JOIN users u ON e.user_id = u.user_id WHERE DATE_TRUNC('month', e.created_at) = '2024-07-01'. Once validated, add the metric spec and query to the team's data catalog with a description, owner, and freshness SLA.
Validated: MAU for Q3 2024 = Jul: 12,487 | Aug: 13,102 | Sep: 12,951

Strengths, Limitations & Common Mistakes

A disciplined approach to metric definition dramatically reduces reporting errors, but it is not without tradeoffs. Understanding where the process shines and where it can break down helps you apply it pragmatically rather than dogmatically.

Tradeoffs of formal metric definition
StrengthsLimitations
Reproducibility — anyone following the spec produces identical results, regardless of tooling or dialectOverhead — writing formal specs for every ad-hoc question slows exploratory analysis
Auditability — the spec serves as a paper trail connecting business intent to SQL logicRigidity — specs can become stale if the underlying data model evolves without updating metric definitions
Testability — each component (filter, grain, aggregation) can be independently unit-testedStakeholder friction — non-technical stakeholders may resist the disambiguation process
Composability — well-defined metrics can be combined into composite KPIs and dashboardsFalse precision — a perfectly specified metric on poor-quality data still produces misleading results

Common Implementation Mistakes

  • Summing distinct counts across time: Adding daily DAU figures to estimate MAU. Because users overlap across days, this always over-counts. The fix: recompute COUNT(DISTINCT user_id) at the monthly grain from raw events.
  • Averaging averages: Computing daily average order values and then averaging those daily averages to get a monthly AOV. This is mathematically incorrect unless each day has the same number of orders. The fix: use SUM(revenue) / COUNT(orders) at the monthly level.
  • Timestamp boundary errors: Using <= '2024-09-30' on a timestamp column, which excludes events on September 30 after midnight. Use < '2024-10-01' for exclusive upper bounds.
  • Ignoring NULLs in aggregations: SQL aggregate functions like AVG and SUM silently ignore NULLs, which can produce correct-looking but misleading results if NULLs represent meaningful missing data.
KEY TAKEAWAY
The most dangerous metric bugs are not syntax errors — your query runs fine and returns a number. The danger lies in the number being wrong but plausible. A formal metric specification is your best defense because it creates a reviewable artifact separate from the code. Think of it as a type system for analytics: it catches semantic errors at specification time rather than after a flawed number has already informed a business decision.

Connection to Semantic Layers & Metrics Engineering

The manual process of writing metric specs and implementing them in SQL is the foundation, but modern analytics engineering has evolved toward programmatic metric layers that codify definitions in configuration files and automatically generate the correct SQL for any requested grain or dimension. Understanding the manual process is essential because these tools encode the same principles — they simply automate the translation step and enforce consistency at scale.

Manual SQL metrics vs. programmatic semantic layers
AspectManual SQL Metrics (This Lesson)Semantic Layer / Metrics Layer
Definition formatWritten spec document + SQL fileYAML/config file with entity, measure, dimensions, and filter fields
Query generationAnalyst writes SQL by handFramework generates SQL at query time based on requested dimensions
Version controlDepends on team disciplineBuilt-in via Git — metric definitions are code
Grain flexibilityNew query needed for each grain (daily, weekly, monthly)Specify grain at query time; framework handles DATE_TRUNC and GROUP BY
Use caseSmall teams, ad-hoc analysis, learning metric designLarge organizations with many consumers querying the same metrics

Tools like dbt Semantic Layer (formerly MetricFlow), Looker's LookML, and Cube.js all implement this pattern. They allow you to define a metric once — specifying its entity, measure, time dimension, and filters — and then query it at any grain or with any dimensional slice without rewriting SQL. This eliminates an entire class of bugs where the same metric is implemented slightly differently in two dashboards. However, mastering the manual approach taught in this lesson is a prerequisite: you need to understand what the tool is doing under the hood to debug it when the generated SQL produces unexpected results.

🔭 Looking Ahead
As you advance into analytics engineering and data modeling, you will encounter derived metrics — metrics defined in terms of other metrics (e.g., revenue per active user = revenue metric ÷ MAU metric). These require careful attention to grain alignment: both component metrics must be computed at the same grain before division. The formal tuple notation M = ⟨ E, F, G, A ⟩ extends naturally to derived metrics by defining composition rules.

Practice Problems

The following problems escalate from conceptual understanding to applied SQL implementation. For problems requiring SQL, assume the schema described in the worked example: an events table (event_id, user_id, event_type, created_at) and a users table (user_id, is_test_account, country_code, signup_date). Also assume an orders table (order_id, user_id, order_total, created_at, status) where status can be 'completed', 'refunded', or 'cancelled'.

PROBLEM 1CONCEPTUAL
Explain why COUNT(DISTINCT user_id) computed at a daily grain cannot be summed to produce a correct monthly COUNT(DISTINCT user_id). What property of the metric makes it non-additive? Provide a concrete numerical example with at least three days.
PROBLEM 2BASIC CALCULATION
Write a metric specification (entity, qualifying event, time grain, aggregation, exclusions) and then the corresponding SQL query for: "Total completed order revenue per calendar week in 2024, excluding refunded and cancelled orders and test accounts."
PROBLEM 3INTERMEDIATE
A product manager asks for the "weekly conversion rate" — the percentage of users who logged in during a week and also made at least one purchase that same week. Write the metric spec and a SQL query using CTEs to separate the numerator and denominator populations. Ensure the metric is computed correctly (not by averaging daily rates).
PROBLEM 4APPLIED
Your company wants to track a 7-day rolling average of daily revenue to smooth out day-of-week effects. Write a SQL query that produces, for each day in Q3 2024, the date, the daily revenue, and the trailing 7-day average revenue (including the current day). Use a window function. Then explain why this rolling average metric cannot be pre-aggregated at a weekly grain.
PROBLEM 5CRITICAL THINKING
Two analysts independently implement a "monthly revenue per active user" metric. Analyst A computes monthly revenue and monthly MAU separately, then divides: SUM(revenue) / COUNT(DISTINCT user_id). Analyst B first computes per-user monthly revenue, then takes the average: AVG(user_monthly_revenue). Under what conditions do these two approaches produce the same result? Under what conditions do they diverge? Which implementation is more appropriate for the metric name, and why? Write the SQL for both approaches and reason about their semantic differences.

Lesson Summary

Defining metrics precisely before implementing them in SQL is the single most impactful practice for producing trustworthy analytics. Every metric can be decomposed into four components: the entity being measured, the qualifying event and filters that determine inclusion, the time grain that sets the temporal resolution, and the aggregation function that reduces rows to a single value. This four-part specification — formalized as M = ⟨ E, F, G, A ⟩ — maps directly onto SQL clauses: FROM/JOIN for the entity, WHERE for the filter, DATE_TRUNC and GROUP BY for the time grain, and the aggregate expression in SELECT for the aggregation.

Critical implementation details include distinguishing additive metrics (like SUM and COUNT) from non-additive metrics (like COUNT DISTINCT and ratios), using exclusive upper bounds for timestamp boundaries, never averaging pre-computed averages, and always recomputing ratio metrics from raw data at the target grain. These principles extend into modern semantic layers and metrics engineering tools, which automate the translation from specification to SQL but still require you to understand the underlying logic.

Varsity Tutors • SQL • Defining & Implementing Metrics