Create
Define a reusable relational interface with CREATE VIEW.
DATA ENGINEERING LAB · SQL VIEW
A VIEW is a stored query presented like a table. In data engineering, its deeper value is not shorter SQL. It creates a governed interface between changing source tables and the people, dashboards and pipelines that depend on them.
Define a reusable relational interface with CREATE VIEW.
Expose useful columns while hiding internal or sensitive details.
Change transformation logic once instead of editing every consumer.
Test grain, uniqueness, nulls and totals before publishing a view.
MENTAL MODEL
When a normal view is queried, the database expands its definition and reads the underlying relations. This is different from a materialized view, which stores query results and must be refreshed.
Always reflects underlying data; query cost is paid when read. Best for abstraction, governance and reusable logic.
Stores computed results; reads can be faster but freshness depends on refresh policy. Best for expensive, repeated aggregation.
Owns persisted rows and a write lifecycle. Use when the dataset needs independent storage, history or incremental loading.
01 · SET UP THE SOURCE
Run the statements in PostgreSQL, DuckDB or another SQL database after adapting types and syntax where necessary.
CREATE SCHEMA IF NOT EXISTS raw;
CREATE SCHEMA IF NOT EXISTS analytics;
CREATE TABLE raw.customers (
customer_id INTEGER PRIMARY KEY,
customer_name VARCHAR(100) NOT NULL,
email VARCHAR(150),
segment VARCHAR(30) NOT NULL
);
CREATE TABLE raw.orders (
order_id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES raw.customers(customer_id),
ordered_at TIMESTAMP NOT NULL,
status VARCHAR(20) NOT NULL,
amount DECIMAL(12,2) NOT NULL
);
INSERT INTO raw.customers VALUES
(1,'Aster Studio','[email protected]','SME'),
(2,'Blue River','[email protected]','Enterprise'),
(3,'Cedar Lab','[email protected]','Education');
INSERT INTO raw.orders VALUES
(101,1,'2026-08-01 09:15','completed',12500.00),
(102,1,'2026-08-03 13:40','cancelled',3200.00),
(103,2,'2026-08-04 10:10','completed',54000.00),
(104,3,'2026-08-05 16:25','pending',7800.00),
(105,2,'2026-08-07 11:30','completed',18500.00);02 · CREATE THE FIRST VIEW
The grain—what one row represents—is part of the contract. State it before writing SQL.
CREATE VIEW analytics.v_completed_orders AS
SELECT
o.order_id,
o.ordered_at,
CAST(o.ordered_at AS DATE) AS order_date,
c.customer_id,
c.customer_name,
c.segment,
o.amount
FROM raw.orders AS o
JOIN raw.customers AS c
ON c.customer_id = o.customer_id
WHERE o.status = 'completed';
SELECT *
FROM analytics.v_completed_orders
ORDER BY ordered_at;03 · BUILD A REUSABLE METRIC
The second view depends on the first. This layering is useful when each layer has a clear responsibility; uncontrolled chains of views become difficult to debug.
CREATE VIEW analytics.v_daily_revenue AS
SELECT
order_date,
COUNT(*) AS completed_orders,
COUNT(DISTINCT customer_id) AS active_customers,
SUM(amount) AS revenue,
AVG(amount) AS average_order_value
FROM analytics.v_completed_orders
GROUP BY order_date;
SELECT *
FROM analytics.v_daily_revenue
ORDER BY order_date;Should pending orders count as revenue? Should refunds reduce the original day or the refund day? SQL cannot decide accounting policy. The view must encode a definition agreed by the data owner.
04 · CHANGE WITHOUT BREAKING CONSUMERS
CREATE OR REPLACE VIEW can update compatible logic while preserving the object name. Engines differ on whether columns may be removed, reordered or have types changed—verify your database.
CREATE OR REPLACE VIEW analytics.v_daily_revenue AS
SELECT
order_date,
COUNT(*) AS completed_orders,
COUNT(DISTINCT customer_id) AS active_customers,
SUM(amount) AS revenue,
AVG(amount) AS average_order_value,
MAX(amount) AS largest_order
FROM analytics.v_completed_orders
GROUP BY order_date;A new source column could silently enter downstream datasets. Explicit columns make schema review and lineage easier.
05 · VALIDATE BEFORE PUBLISHING
Run these checks after source or transformation changes. In production, automate them with dbt, orchestration or a data-quality framework.
SELECT order_id, COUNT(*)
FROM analytics.v_completed_orders
GROUP BY order_id
HAVING COUNT(*) > 1;Expected: zero rowsSELECT COUNT(*) AS invalid_rows
FROM analytics.v_completed_orders
WHERE customer_id IS NULL
OR amount IS NULL
OR amount < 0;Expected: 0SELECT
(SELECT SUM(amount) FROM raw.orders
WHERE status='completed') AS source_total,
(SELECT SUM(revenue)
FROM analytics.v_daily_revenue) AS view_total;Expected: equal totals06 · DATA ENGINEERING VALUE
Views are lightweight architecture. Their value becomes visible when source owners, data engineers, analysts, application developers and governance teams need to share data without sharing every internal detail. The view becomes a small but explicit agreement about what others may rely on.
Hide how data is stored; publish what the business concept means.
The order system stores status as C, P and X across orders, status_history and customer tables. An analyst should not need to learn three schemas and decode internal codes before answering “How much did we sell yesterday?”
order_id, order_date, customer_segment, order_status, net_amount with readable values and an explicit grain.
Result: Source tables may remain optimized for transactions while consumers work with a stable analytical vocabulary. Abstraction does not remove the need for lineage—the view must still show where each field came from.
Write difficult logic once, review it once, then let many consumers reuse it.
Finance excludes cancelled orders, Marketing includes paid orders awaiting fulfilment, and Operations uses shipment date instead of order date. Each dashboard contains a slightly different 40-line query, so three “revenue” numbers appear in the same meeting.
Joins, status filters, casts, currency conversion and deduplication are reviewed as one transformation instead of copied into every notebook and dashboard.
Result: A correction is made once and reaches every consumer on the next query. Reuse is safe only when the shared definition really represents a shared concept; different business meanings may deserve different views.
Turn an informal query into an interface with a name, grain, key and expectations.
A machine-learning pipeline assumes one row per customer, while a new join silently creates one row per customer per address. The pipeline still runs, but customers with several addresses receive more weight in training.
One row per customer; customer_id is unique and non-null; amount is THB; data is ready daily by 07:00; owner and breaking-change process are documented.
Result: Downstream teams know what may be assumed and can automate tests around it. A view alone is not a complete contract—the promise also needs documentation, ownership, freshness monitoring and change communication.
Give people the data needed for the task without handing them the entire source.
A university analyst needs enrolment counts by faculty and year. The student table also contains names, personal email, telephone numbers and national identifiers. Granting SELECT on the base table exposes far more than the analysis requires.
Faculty, academic year and aggregated counts—or approved pseudonymous identifiers—while direct access to the base table remains revoked.
Result: The access surface becomes smaller and easier to audit. But database ownership rules, row-level policies, small-group inference and permissions must still be tested with the consumer role.
Make the same term mean the same thing across tools.
“Active customer” means logged in within 30 days to Product, purchased within 90 days to Marketing, and has an open contract to Finance. None is inherently wrong, but an unlabeled metric lets users compare values that answer different questions.
v_product_active_customer_30d, v_purchasing_customer_90d and definitions approved by the relevant owners. Consumers select a concept instead of inventing another one.
Result: Meetings move from arguing about whose SQL is correct to choosing which business question is being answered. Consistency does not mean forcing every context into one universal metric.
Change the machinery behind a stable interface, then migrate consumers deliberately.
Orders move from a legacy database to a new platform. Table names, timestamp types and customer keys change, but twelve dashboards, two scheduled exports and one model still query the old analytical shape. A big-bang switch risks breaking all of them together.
Existing column names, types and grain while reading from the new source. A v2 view exposes the improved schema so consumers can migrate and validate one by one.
Result: Migration risk is spread across observable steps and rollback remains possible. The compatibility view needs an expiry date, or a temporary bridge becomes permanent technical debt.
A good view does not merely make SQL shorter. It lets the producer change internal implementation while giving the consumer a clear, testable and appropriately governed promise.
07 · GOVERN ACCESS
The exact security semantics depend on the database. Ownership, invoker/definer rights, row-level security and inference risks must be reviewed—not assumed.
-- Example role names; adapt to your environment.
REVOKE ALL ON raw.customers, raw.orders FROM analyst_role;
GRANT USAGE ON SCHEMA analytics TO analyst_role;
GRANT SELECT ON analytics.v_completed_orders,
analytics.v_daily_revenue
TO analyst_role;Test permissions using the consumer role. Review whether predicates, functions, error messages or aggregates could reveal protected information.
CHALLENGES
Create v_customer_lifetime_value with one row per customer; include customers with no completed orders.
Add a refund table and define net revenue. Write the business assumption before writing SQL.
Create an analyst-safe view that contains customer_id and segment but excludes name and email.
Use EXPLAIN to compare a direct query and a query through the view. Explain why a view is not a performance guarantee.
Design v_daily_revenue_v2 without breaking v_daily_revenue. State a deprecation plan.
Decide whether one expensive monthly aggregate should be a view, materialized view or table—and justify freshness, cost and recovery.
FINAL CHECK