1. Home
  2. Blog
  3. Databases & SQL

Databases & SQL

SQL Views Explained: Create, Use and Avoid the Pitfalls, With Examples

A view is a saved query that you can use like a table. Learn how to create one, why views keep reports consistent, what they do and do not do for performance, and the traps that break them.

8 min read
SQL views explained: create, use and avoid the pitfalls, with examples

Key takeaways

  • A view is a named, stored query that you can select from like a table.
  • A normal view stores no data, so it always shows current results.
  • Views simplify complex queries, keep business logic consistent and can restrict access to columns.
  • They do not make a query faster by themselves, though materialized views can.

The same twenty-line report query has been copied into five dashboards, and each copy has drifted a little. One counts cancelled orders, while another does not. SQL views solve this by defining that logic once, in the database, under a clear name.

We ran every example below in SQLite 3.50, and the results shown come from that run. Views are standard SQL, but details such as updating through a view, materialized views and dependency handling vary between databases, and we point those out.

What is a view?

A view is a SELECT statement saved in the database with a name. When you query the view, the database runs that saved query. It acts like a table for reading, but a normal view does not hold its own copy of the data.

Think of a saved filter in a spreadsheet. The filter is not a second copy of the sheet. It is a way of looking at the same data, which always reflects the latest values.

The sample tables

CREATE TABLE customers (
  id INT PRIMARY KEY, name TEXT, city TEXT, email TEXT, credit_limit INT
);
CREATE TABLE orders (
  id INT PRIMARY KEY, customer_id INT, total INT, status TEXT
);

INSERT INTO customers VALUES
 (1,'Asha','Pune','asha@example.com',50000),
 (2,'Ben','Delhi','ben@example.com',20000),
 (3,'Chitra','Pune','chitra@example.com',80000);

INSERT INTO orders VALUES
 (101,1,1200,'paid'), (102,1,800,'paid'),
 (103,2,300,'pending'), (104,3,2500,'paid');

Create a view

This view summarizes each customer’s orders. It hides a join, grouping and a CASE expression behind one simple name:

CREATE VIEW customer_sales AS
SELECT c.id, c.name, c.city,
       COUNT(o.id) AS orders,
       COALESCE(SUM(CASE WHEN o.status = 'paid' THEN o.total END), 0) AS paid_total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name, c.city;

Now anybody can use it, without knowing how it works:

SELECT * FROM customer_sales ORDER BY paid_total DESC;

-- 3 | Chitra | Pune  | 1 | 2500
-- 1 | Asha   | Pune  | 2 | 2000
-- 2 | Ben    | Delhi | 1 |    0

SELECT name, paid_total FROM customer_sales WHERE city = 'Pune';

-- Asha   | 2000
-- Chitra | 2500

Ben appears with zero, because the view uses a LEFT JOIN and COALESCE. Decisions like this, made once in the view, are exactly what keep reports consistent. To refresh your understanding of the NULL handling, see SQL NULL values.

Views are always current

Since a normal view runs its query every time, new data appears immediately. We added a paid order for Ben and queried the view again:

INSERT INTO orders VALUES (105, 2, 900, 'paid');

SELECT name, paid_total FROM customer_sales WHERE name = 'Ben';

-- Ben | 900

There was no refresh step and no copy to keep in sync. This is the key difference from saving query results into a separate table.

Hiding columns

Views are often used to expose only the columns a person or application needs. Here, email addresses and credit limits are left out:

CREATE VIEW public_customers AS
SELECT id, name, city FROM customers;

-- columns: id, name, city

In databases with permissions, such as PostgreSQL, SQL Server and MySQL, you can grant a reporting user access to the view but not the underlying table. SQLite has no user accounts, so we could only show the column restriction itself. Treat a view as one layer of protection. Sensitive data still needs proper access control, and some databases offer row-level security for filtering rows by user.

Performance: views are not a speed boost

A common myth is that views make queries faster. A normal view is replaced by its definition when you query it, so the database does the same work as if you had written the full query yourself. Our execution plan showed the view’s join and grouping being run as part of the query:

EXPLAIN QUERY PLAN SELECT * FROM customer_sales WHERE city = 'Pune';

-- CO-ROUTINE customer_sales
-- SCAN c ...
-- SEARCH o USING AUTOMATIC COVERING INDEX (customer_id=?) LEFT-JOIN
-- USE TEMP B-TREE FOR GROUP BY
-- SCAN customer_sales

So a view over a slow query is still a slow query. Speed comes from indexes and good query design, as covered in SQL query optimization and SQL indexes explained. Watch out for views built on other views, too. Layers of nesting make it easy to join the same large table several times without noticing.

Materialized views

A materialized view does store its results, like a cached table. Reads are fast, but the data is only as fresh as the last refresh.

-- PostgreSQL
CREATE MATERIALIZED VIEW daily_sales AS
SELECT order_date, SUM(total) AS revenue FROM orders GROUP BY order_date;

REFRESH MATERIALIZED VIEW daily_sales;
Feature Normal view Materialized view
Stores data No Yes
Freshness Always current As of the last refresh
Read speed Same as the query Fast
Storage cost None Yes
Good for Simplifying, consistency Heavy reports and dashboards

Support varies. PostgreSQL and Oracle have materialized views, and SQL Server offers indexed views with their own rules. SQLite and MySQL lack them, so people use a regular summary table refreshed on a schedule. We did not run the PostgreSQL example for this article.

Tip: Before creating a materialized view, decide how stale the data is allowed to be. “Refreshed every hour” must be acceptable to the people reading the report, so tell them.

Can you update a view?

It depends on the database and the view. A simple view over a single table, without grouping or DISTINCT, can often be updated in PostgreSQL, MySQL and SQL Server, and the change passes through to the table. A view with joins or aggregates generally cannot, because the database cannot tell which row to change.

SQLite does not allow direct writes to views at all. Our attempt failed:

INSERT INTO customer_sales (name) VALUES ('X');

-- cannot modify customer_sales because it is a view

Some databases support INSTEAD OF triggers to make a view writable, and many offer WITH CHECK OPTION, which stops writes that would not be visible through the view. In practice, most teams keep views read-only and write to the tables.

Dependencies and breakage

A view depends on the tables it reads from. We created a view on a table, then dropped the table:

CREATE TABLE ghost AS SELECT 1 AS x;
CREATE VIEW v_ghost AS SELECT x FROM ghost;
DROP TABLE ghost;

SELECT * FROM v_ghost;
-- no such table: main.ghost

SQLite allowed the drop and only failed later, when the view was used. Other databases, such as PostgreSQL, refuse to drop a table that a view depends on, unless you add CASCADE, which removes the view as well. Either way, renaming or dropping a column can break views, so check them in your regression testing after schema changes.

There is also a subtle trap with SELECT * in a view. We created CREATE VIEW star AS SELECT * FROM customers, then added a column to the table. In SQLite, the view picked up the new column. Other databases, including PostgreSQL, expand the column list when the view is created, so new columns do not appear. So list the columns explicitly. It documents the view, and it behaves the same everywhere.

Managing views

Task Common syntax
Create CREATE VIEW name AS SELECT ...
Replace CREATE OR REPLACE VIEW (PostgreSQL, MySQL, Oracle)
Replace in SQL Server CREATE OR ALTER VIEW
Remove DROP VIEW name
Remove safely DROP VIEW IF EXISTS name

Dropping a view only removes the saved query. In our test, the customers table still had all three rows after dropping public_customers. Keep view definitions in version control along with your migrations, so changes are reviewed like any other code.

View vs CTE vs table

Option Lifetime Stores data Best for
View Until dropped No Shared logic used by many queries
CTE One statement No Organizing a single complex query
Materialized view Until dropped Yes Fast reads of costly results
Table Until dropped Yes The actual data

Common mistakes

  • Expecting a speed boost. A normal view runs its query each time.
  • Using SELECT * in views. Behaviour differs between databases when tables change.
  • Deep nesting. Views built on views hide expensive joins.
  • Using ORDER BY inside a view. Order is not guaranteed when you query it, so sort in the outer query.
  • Unclear names. A prefix such as v_, or a clear noun like customer_sales, helps.
  • Forgetting views during migrations. Test them after every schema change.

Final thoughts

Views are a simple way to give important logic one home in the database. Use them to keep reports consistent and to hide unneeded columns, not as a performance trick. To start, find a query that appears in several places in your code, turn it into a view with explicit columns, and point each caller at it.

Frequently asked questions

What is a view in SQL?

A view is a saved SELECT query with a name. You query it like a table, and the database runs the underlying query each time, so results are always current.

Does a SQL view store data?

A normal view does not store data, only the query. A materialized view does store results, and must be refreshed to stay up to date.

Are views faster than tables?

No. A normal view does the same work as its underlying query. Materialized views can speed up reads, at the cost of storage and freshness.

Can you insert into a view?

Sometimes. Simple single-table views are often updatable in PostgreSQL, MySQL and SQL Server. Views with joins or grouping usually are not, and SQLite does not allow it.

What happens to a view if the table is dropped?

It depends on the database. Some refuse to drop the table while a view depends on it, while others allow it and the view fails when queried.

Keep learning

Work with Stack Quora

Need help with testing, WordPress or SEO?

We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.

Keep reading

More guides on testing, AI & modern development

Enjoyed this article? Pick a topic and jump straight into our newest tutorials.

Artificial Intelligence

LLM Structured Output Explained: Getting Reliable JSON From AI Models

LLM structured output explained with validation we ran in Pydantic: JSON mode and schemas, constrained decoding, the ways model output…

Read article →
DevOps

SSH Keys Explained: How They Work, ssh-keygen, Ed25519 and Safe Habits

SSH keys explained with keys we generated: public vs private keys, ssh-keygen, Ed25519 vs RSA, fingerprints, passphrases, ssh-agent, authorized_keys, config…

Read article →
DevOps

HTTP Caching Explained: Cache-Control, ETag and 304 Responses, With a Live Test

HTTP caching explained with a small server we ran: Cache-Control directives, max-age, no-cache vs no-store, ETag revalidation and 304 responses,…

Read article →
Test Automation

API Contract Testing Explained: Catch Breaking Changes Before They Ship

API contract testing explained with a schema check we ran: what a contract is, consumer-driven contracts, Pact, OpenAPI, which changes…

Read article →
Software Testing

Risk-Based Testing Explained: Score Risks, Build a Matrix and Test What Matters

Risk-based testing explained step by step: identifying risks, scoring likelihood and impact, building a risk matrix, mapping risk to test…

Read article →
Databases & SQL

SQL Constraints Explained: NOT NULL, UNIQUE, CHECK and FOREIGN KEY, Tested

SQL constraints explained with tests we ran: NOT NULL, UNIQUE, CHECK, DEFAULT, PRIMARY and FOREIGN KEY, the NULL loopholes, SQLite's…

Read article →
Databases & SQL

SQL Date Functions Explained: Adding, Comparing and Grouping Dates Without Bugs

SQL date functions explained with queries we ran: adding intervals, month-end surprises, date differences, the BETWEEN trap, grouping by day,…

Read article →
Software Development

Python asyncio Explained: async, await, gather and TaskGroup With Real Timings

Python asyncio explained with code we timed: coroutines, await, gather, TaskGroup, error handling, timeouts, limiting concurrency and the blocking-call mistake…

Read article →
Software Development

Debounce vs Throttle in JavaScript: How They Work, When to Use Each, Tested

Debounce vs throttle in JavaScript explained with timings we measured in Node.js: how each works, simple implementations, leading and trailing…

Read article →
Software Development

Python Context Managers Explained: The with Statement, contextlib and Real Uses

Python context managers explained with code we ran: how with works, __enter__ and __exit__, contextlib.contextmanager, suppress, ExitStack, database transactions and…

Read article →
Artificial Intelligence

LLM Temperature and Top-p Explained: How AI Models Choose Their Next Word

LLM temperature and top-p explained with a simulation we ran: how probabilities are reshaped, what top-p keeps, recommended settings by…

Read article →
DevOps

YAML Explained: Syntax, the Norway Problem and Other Traps We Tested

YAML explained with a parser test: basic syntax, lists and maps, the Norway problem, versions turning into floats, octal zip…

Read article →
DevOps

Cron Jobs Explained: Syntax, Real Schedules and the Mistakes That Break Them

Cron jobs explained with schedules we verified: the five fields, operators, real examples, the day-of-month OR day-of-week trap, environment problems,…

Read article →
Software Testing

Bug Life Cycle in Software Testing: Every Status, Who Owns It and Common Mistakes

The bug life cycle explained: every defect status from New to Closed, who owns each step, rejected, deferred and reopened…

Read article →
Test Automation

Property-Based Testing in Python: Hypothesis Finds the Bugs Your Examples Miss

Property-based testing explained with Hypothesis tests we ran: properties vs examples, strategies, shrinking, the real bugs it found in our…

Read article →
Databases & SQL

SQL Pagination Explained: OFFSET vs Keyset, With a Million-Row Test

SQL pagination explained with a million-row test we ran: LIMIT and OFFSET, why deep pages get slow, keyset (cursor) pagination,…

Read article →
Databases & SQL

SQL EXISTS vs IN: Differences, the NOT IN NULL Trap and Performance

SQL EXISTS vs IN explained with queries we ran: how each works, why NOT IN returns no rows when a…

Read article →
Software Development

Python Logging Explained: Levels, Handlers, Formatters and Mistakes to Avoid

Python logging explained with code we ran: levels, basicConfig quirks, named loggers, handlers and formatters, logger.exception, lazy formatting, duplicate logs…

Read article →
Software Development

JavaScript Map vs Object: Key Differences, Pitfalls and a Benchmark

JavaScript Map vs Object explained with code we ran in Node.js: key types, ordering, the prototype trap, JSON, size, WeakMap…

Read article →
Software Development

Python Type Hints Explained: Annotations, mypy and the Errors It Catches

Python type hints explained with a real mypy run: basic annotations, Optional and unions, TypedDict, Literal, Protocol, generics, Callable, strict…

Read article →
Artificial Intelligence

LLM Tokens Explained: How AI Models Read Text, Count Costs and Fill Context Windows

LLM tokens explained with a tokenizer we ran: how text is split, why counts differ by language and tokenizer, context…

Read article →
DevOps

Semantic Versioning Explained: MAJOR.MINOR.PATCH, Ranges and Release Rules

Semantic versioning explained with code we ran: MAJOR.MINOR.PATCH rules, pre-releases, correct version sorting, caret and tilde ranges, 0.x versions, lockfiles…

Read article →
DevOps

Git Reset vs Revert: How to Undo Commits Safely, With a Tested Example

Git reset vs revert explained with a repository we built: soft, mixed and hard reset, revert commits, reverting merges, recovering…

Read article →
Software Testing

The Test Pyramid Explained: Layers, Ratios and When to Break the Shape

The test pyramid explained: unit, integration and end-to-end layers, sensible ratios, the ice cream cone anti-pattern, the testing trophy and…

Read article →
Test Automation

pytest Fixtures Explained: Scope, yield Teardown, conftest.py and Factories

pytest fixtures explained with a suite we ran: how fixtures work, scopes, yield teardown, conftest.py, factory fixtures, parametrized fixtures, built-ins…

Read article →
Databases & SQL

SQL Upsert Explained: INSERT ON CONFLICT, MERGE and the REPLACE Trap

SQL upsert explained with queries we ran: INSERT ON CONFLICT DO UPDATE and DO NOTHING, the excluded row, conditional updates,…

Read article →
Databases & SQL

SQL LIKE Operator Explained: Wildcards, Case Sensitivity and Performance

The SQL LIKE operator explained with queries we ran: % and _ wildcards, escaping, NOT LIKE and NULL, case sensitivity…

Read article →
Artificial Intelligence

AI Hallucinations Explained: Why Language Models Make Things Up and How to Reduce It

AI hallucinations explained: what they are, why language models produce them, common types, how to reduce them with grounding and…

Read article →
Test Automation

Mocking in Python: unittest.mock, patch and the Mistakes That Make Tests Lie

Mocking in Python with unittest.mock, tested: Mock and return_value, side_effect, patching where a name is used, spec and autospec, mocking…

Read article →
Software Testing

Code Coverage Explained: Line vs Branch Coverage and What 100% Really Means

Code coverage explained with a real coverage.py run: line, branch and condition coverage, reading a report, why 100% coverage can…

Read article →
Artificial Intelligence

What Are AI Agents? How They Work, With a Simple Agent Loop You Can Run

What are AI agents? A plain guide to the agent loop, tools, memory and guardrails, with a small Python loop…

Read article →
Test Automation

TDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each

TDD vs BDD explained with one discount feature built both ways: red-green-refactor with code we ran, Given-When-Then scenarios, tools, trade-offs…

Read article →
Software Testing

User Acceptance Testing (UAT): Process, Checklist and Real Examples

User acceptance testing explained: who runs it, how it differs from QA, a seven-step process, a UAT test case template,…

Read article →
Artificial Intelligence

What Are Embeddings? Vectors and Cosine Similarity Explained Simply

What are embeddings? A plain-language guide to vectors, cosine similarity and semantic search, with a small example we ran, common…

Read article →
Test Automation

Flaky Tests: What Causes Them and How to Fix Them for Good

Flaky tests explained with demos we ran: the main causes, how to confirm and diagnose one, proper fixes for timing,…

Read article →
Software Testing

Load Testing vs Stress Testing: Differences, Metrics and k6 Examples

Load testing vs stress testing explained: what each proves, spike and soak tests, the metrics that matter, why averages mislead,…

Read article →