Question bank — Views — creating, updatable views
For the deeper machinery behind these, see SQL SELECT and JOINs, Materialized Views, Database Security and GRANT, Triggers, and Query Optimization and View Merging.
The core picture: one-to-one vs many-to-one
The single idea behind every updatability rule is a mapping — an arrow from each view row back to the base row(s) it came from. The figure below shows the two shapes this arrow can take.

Look at the left panel: a plain filtering view. Each green view row has exactly one blue arrow to a single base row — a one-to-one map. You can invert the arrow, so writes translate cleanly. The right panel: an aggregating view. Many blue base rows funnel into one orange summary row — a many-to-one map. There is no single arrow to follow backwards, so the engine cannot know which base row your edit means. That is why aggregation kills updatability.
The next figure shows the same collapse happening for the three other "killers" — DISTINCT, multi-table JOIN, and UNION — so you can see why each breaks the arrow.

Refer back to these two pictures whenever an answer below says "the mapping is undefined."
True or false — justify
A standard view stores its own copy of the query result.
A view is always in sync with its base tables.
SELECT each time, any change to base data is reflected instantly. A materialized view is the exception: it can be stale until refreshed.Querying a view is always faster than writing the raw query.
A view can hide columns from a user who only has access to the view.
SELECT never lists salary, a user granted SELECT on the view cannot reference salary at all; the column simply does not exist in the view's schema. This is the security use-case.Every SELECT you can write can be turned into an updatable view.
GROUP BY, DISTINCT, UNION and multi-table joins break that mapping.WITH CHECK OPTION makes a view faster.
INSERT/UPDATE through the view to keep satisfying the view's WHERE, blocking writes that would land outside the view.Deleting a row through an updatable view leaves the base table untouched.
DELETE propagates straight to the base table and removes the real row.A view with AVG(salary) and GROUP BY dept_id is read-only.
All views containing a JOIN are strictly non-updatable in every database.
WITH CHECK OPTION prevents anyone from ever seeing a row outside the view.
WHERE; the option cannot police the base table.A window function like ROW_NUMBER() OVER (...) in the SELECT list keeps the view updatable.
Spot the error
CREATE VIEW v AS SELECT price*1.1 AS marked FROM items; then UPDATE v SET marked = 50; — what breaks?
marked is an expression, not a plain base column, so it is not an updatable column. The engine can't invert price*1.1 = 50 unambiguously in general, so the UPDATE is rejected.Given CREATE VIEW cheap_items AS SELECT item_id, name, price FROM items WHERE price < 100; (no check option), then INSERT INTO cheap_items VALUES (50,'Mug',150); — what's wrong?
items table, but the new row has price = 150, violating price < 100, so it instantly vanishes from the view. Nothing errored — that silent disappearance is the trap. Add WITH CHECK OPTION to reject it."I granted SELECT on the base table to the intern so they can use my public view." — what did you leak?
salary column too. Security via views works only if the intern is granted on the view, not the base table.CREATE VIEW v AS SELECT DISTINCT dept_id FROM employees; then DELETE FROM v WHERE dept_id=3; — why can't the DB obey?
DISTINCT collapses many base rows into one view row, so "delete this one view row" maps to many base rows (see the second figure). The engine can't tell which employees you meant, so the view is non-updatable.A UNION view A UNION B; you INSERT a row — where should it go?
A, B, or both, so set-operation views are non-updatable. You'd need an INSTEAD OF trigger to specify the target.Why questions
Why is a view called "virtual"?
Why does an aggregate view break updatability, in one sentence?
Why grant SELECT on a view instead of on the base table?
Why can changing the schema break apps that read the base table but not apps that read a view?
Why does the optimiser "merge" a view into your query?
Why is INSTEAD OF a trigger and not just a flag?
Edge cases
A view whose SELECT has no WHERE at all — is it updatable?
WHERE exists.An updatable view that omits a NOT NULL base column with no default — can you INSERT through it?
NULL for a required column and reject the row. The view exposes too few columns to build a valid base row, so inserts fail even though updates may work.You UPDATE a row through a view with WITH CHECK OPTION so the row would leave the view's WHERE. Result?
A view v2 is built on top of a filtering view v1, and only v2 has WITH LOCAL CHECK OPTION. A write through v2 satisfies v2's WHERE but violates v1's. Allowed?
LOCAL checks only the predicates of the view where the option is declared (v2), ignoring underlying views. The row can silently fall outside v1.Same stack, but v2 has WITH CASCADED CHECK OPTION. Same write — allowed?
CASCADED checks the predicate of v2 and every underlying view (v1 too). The write must satisfy all layers, so violating v1 blocks it. CASCADED is the SQL default when neither word is given.A materialized view vs a standard view when base data changes right now — which reflects it?
Can a single-table view that selects a strict subset of columns still be deleted from?
DELETE and UPDATE (on the exposed columns) translate cleanly to the base table.Is a view that selects every column of one table via SELECT * updatable?
Recall One-line self-test
If you can point at exactly one base row for each view row, and every column you write is a real base column, the write goes through — otherwise the engine has no unique answer and refuses. ::: This single principle explains all the updatability rules above.