Menu

SQLite Views: CREATE VIEW, Temporary Views, and INSTEAD OF

How views work in SQLite — saving queries as virtual tables, when to use temporary views, and why SQLite's views are read-only by default.

This page includes runnable editors — edit, run, and see output instantly.

A View Is a Saved Query

A view is a SELECT statement with a name. Once you've created one, you can query it like a table — but nothing is stored. Every time you read from a view, SQLite runs the underlying query fresh.

paid_orders looks and behaves like a table. It has columns, you can SELECT from it, you can join it. But under the hood, every query expands to the original WHERE status = 'paid' filter.

That's the whole mental model: a view is an alias for a query.

Why Views Are Useful

The main win is naming. A complicated query gets a short, descriptive name, and the rest of your code stays readable:

Without the view, every caller would write the GROUP BY themselves — and any one of them could get the filter wrong. With it, the aggregation is defined once. Callers just ask for customer_totals and add whatever extra filters they need on top.

Views also work as a permission-style boundary. If a query shouldn't expose a password_hash column, build a view that selects everything except that column and have application code use the view.

CREATE VIEW Syntax

The full form:

CREATE [TEMPORARY] VIEW [IF NOT EXISTS] view_name [(column_aliases)] AS
SELECT ...;

A few things worth knowing:

  • IF NOT EXISTS skips creation silently if the view already exists.
  • TEMPORARY (or TEMP) creates a view that vanishes when the connection closes.
  • Column aliases in parentheses let you rename the view's columns without touching the underlying SELECT.

The view exposes friendlier names (item, dollars) without renaming columns in the source table.

Replacing and Dropping Views

SQLite doesn't have CREATE OR REPLACE VIEW or ALTER VIEW. To change a view's definition, drop it and recreate it:

DROP VIEW IF EXISTS active_orders; is the safe form — it won't error if the view isn't there. Dropping a view never affects the underlying tables; you're only deleting the saved query.

Temporary Views

A TEMP VIEW exists only for the current database connection. When the connection closes, the view is gone. Useful for ad-hoc analysis sessions where you don't want to leave definitions behind:

Temp views also let you shadow a query name without committing to it in your schema — handy during exploration.

Views Are Read-Only by Default

This is the most important gotcha. You can't INSERT, UPDATE, or DELETE through a view directly:

sqlite> INSERT INTO paid_orders (customer, amount) VALUES ('Eve', 50);
Runtime error: cannot modify paid_orders because it is a view

The fix is INSTEAD OF triggers. You write a trigger that fires instead of the write attempt and translates it into a real operation on the underlying table:

The view stays a view — but writes against it now have somewhere to go. We'll cover triggers properly in the next page.

No Materialized Views — Roll Your Own

Some databases let you cache a view's results on disk and refresh them on demand. SQLite doesn't. Every read of a view re-executes the underlying query. For most workloads that's fine — SQLite is fast and the query planner is good. For expensive aggregations queried many times, build a real table and keep it in sync yourself:

You'd then refresh the cache on a schedule, or wire up triggers on orders to keep it current. It's manual work — but it's also the only option in SQLite.

Listing Views

The metadata for views lives in sqlite_master alongside tables and indexes:

The sql column gives you back the original CREATE VIEW statement — useful when you've forgotten what a view does. In the CLI, .schema view_name prints the same thing more cleanly.

When to Reach for a View

Views earn their keep when:

  • A non-trivial query gets reused in three or more places. Naming it once beats copy-pasting.
  • You want to expose a curated subset of columns or rows to part of an application.
  • An aggregation is conceptually a single thing (monthly_sales, active_users) that callers should treat as a noun.

Skip the view when:

  • The query is used in exactly one place. Inline it.
  • Performance matters and the underlying query is expensive — you're paying that cost on every read. Cache into a real table instead.
  • The view depends on another view that depends on another view. SQLite handles nesting fine, but a chain of three or four views makes the actual SQL hard to follow during debugging.

Next: Triggers

Views and triggers come up together a lot — the INSTEAD OF pattern that makes views writable is one of the main reasons triggers exist. Triggers are also useful on their own for audit logging, cascading updates, and enforcing invariants. That's the next page.

Frequently Asked Questions

What is a view in SQLite?

A view is a saved SELECT statement that you can query like a table. It doesn't store any data — every time you read from it, SQLite re-runs the underlying query. Views are useful for naming a complex query once and reusing it everywhere, or for hiding columns that callers shouldn't see.

Can you INSERT or UPDATE through a view in SQLite?

Not directly. Views in SQLite are read-only — INSERT, UPDATE, and DELETE against a view will fail. You can make a view writable by attaching INSTEAD OF triggers that translate the write into operations on the underlying tables.

Does SQLite support materialized views?

No. SQLite only has regular (virtual) views — the query runs every time you read from the view. If you need cached results, create a real table and refresh it yourself, or use a trigger to keep it in sync with the source tables.

How do you list all views in a SQLite database?

Query sqlite_master: SELECT name FROM sqlite_master WHERE type = 'view';. In the CLI, .schema shows the CREATE VIEW statements and .tables lists views alongside tables.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED