Module: 14 — Views Previous: 07 — View Performance · Next: 09 — Interview Guide
This file steps back from single-View examples to full architecture: how a real analytics team lays out a View hierarchy across raw, staging, and reporting layers, and where MySQL’s lack of native materialized Views changes the design compared to a warehouse-native platform.
Interviews for Analytics Engineering roles rarely ask “write a View” in isolation — they ask “design the reporting layer for X,” expecting a candidate to reach for layered Views, discuss materialization tradeoffs, and reason about ownership and performance simultaneously. This file is deliberately architecture-first.
A SaaS company needs Finance to see MRR (Monthly Recurring Revenue) and Product to see churn — both derived from the same saas_subscriptions table, with materially different business rules (Finance excludes trialing accounts from MRR; Product includes them in churn cohort analysis).
Layer 1 — Raw (base tables, never queried directly by BI):
saas_subscriptions, saas_customers, saas_plans
Layer 2 — Staging Views (clean, standardize, no business rules yet):
vw_stg_subscriptions (NULL handling, type casting, status normalization)
Layer 3 — Reporting Views (business rules, one per governed metric):
vw_mrr_by_month (Finance definition: excludes TRIALING)
vw_churn_cohort (Product definition: includes TRIALING)
Layer 4 — BI Tool
points at Layer 3 exclusively, never Layer 1 or 2
This is structurally identical to dbt’s staging/ → intermediate/ → marts/ convention — dbt materializes some of these as Views and some as physical tables depending on cost/freshness tradeoffs, but the layering discipline is the same regardless of tooling.
CREATE OR REPLACE VIEW vw_stg_subscriptions AS
SELECT
subscription_id,
customer_id,
UPPER(TRIM(status)) AS status, -- normalize inconsistent casing/whitespace
COALESCE(mrr_amount, 0) AS mrr_amount,
plan_started_at,
plan_ended_at
FROM saas_subscriptions;
CREATE OR REPLACE VIEW vw_mrr_by_month AS
SELECT
DATE_FORMAT(plan_started_at, '%Y-%m') AS revenue_month,
SUM(mrr_amount) AS total_mrr
FROM vw_stg_subscriptions
WHERE status NOT IN ('TRIALING', 'CANCELLED')
GROUP BY DATE_FORMAT(plan_started_at, '%Y-%m');
No new syntax in this file — this is a composition and architecture exercise using everything from Modules 01–07.
SELECT only on Layer 3.MySQL 8.0 has no native materialized View. Postgres (CREATE MATERIALIZED VIEW), Snowflake, and BigQuery all support a View variant that physically stores its result set and must be explicitly (or automatically, on some platforms) refreshed. The MySQL-native workarounds are:
EVENT or an external ETL/orchestration job (shown in Module 07).The conceptual tradeoff is identical everywhere: a regular View trades storage cost for always-current data and re-computation cost on every read; a materialized View (or MySQL’s manual summary-table equivalent) trades storage and staleness for read speed.
Enterprise reporting architectures typically version-control every View definition as a .sql file (exactly as this repository does), deploy them through CI/CD alongside schema migrations, and enforce the layering convention (raw → staging → reporting → BI) as a code-review policy, not just a suggestion.
Layered View architectures compound TEMPTABLE costs the same way discussed in Module 07 — production teams frequently promote the staging layer to physical tables (via ETL) once query volume justifies it, keeping only the reporting layer as true Views.
| Mistake | Consequence |
|---|---|
| BI tool granted direct access to raw tables “just this once” | Layering discipline erodes; inconsistent metrics return |
| No staging layer, business rules mixed with cleaning logic | Reporting Views become unreadable and hard to test independently |
| Assuming MySQL has materialized Views | Design breaks when ported from Postgres/Snowflake documentation examples |
Production View architecture is layered: raw tables are never exposed directly, staging Views normalize data, and reporting Views encode governed, documented business rules for BI consumption. MySQL’s lack of native materialized Views means the read-speed/staleness tradeoff is handled manually via summary tables rather than a built-in refresh mechanism.
WITH CHECK OPTION where applicable.EVENT) that would keep a MySQL “materialized view equivalent” of vw_mrr_by_month updated hourly.