Advanced Views & Virtual Tables
Build updatable views, security views, recursive views, and API-layer views for reusable query logic.
๐ฏ What You'll Learn
- Updatable views with WITH CHECK OPTION
- Security barrier views for safe data access
- Recursive views for hierarchies and trees
- INSTEAD OF triggers for multi-table views
- API views as a stable interface layer
โ๏ธ Updatable Views
Updatable views let you INSERT, UPDATE, and DELETE through them as if they were tables. PostgreSQL auto-detects when a view is simple enough to update directly.
๐ก Pro Tip โ WITH CHECK OPTION
Always add WITH CHECK OPTION to filtered views. Without it, you can INSERT a row through the view that the view itself can't display โ a confusing "disappearing insert" bug.
Updatable Views
INSERT, UPDATE, DELETE through views with check option
-- UPDATABLE VIEWS: INSERT/UPDATE/DELETE through a view
-- Simple updatable view (automatically updatable):
CREATE VIEW v_active_employees AS
SELECT id, name, email, department, salary
FROM employees
WHERE status = 'active';
-- You can INSERT through the view:
INSERT INTO v_active_employees (name, email, department, salary)
VALUES ('New Hire', 'new@company.com', 'Engineering', 85000);
-- Inserts into the underlying employees table
-- UPDATE through the view:
UPDATE v_active_employees SET sala
...๐ Security Views
Security views act as a data access layer โ users query the view (which masks or filters data) instead of the raw table. Use security_barrier to prevent optimizer-based data leaks.
Security Views
Data masking, security barrier, and row-level access
-- SECURITY VIEWS: control data access through views
-- View that masks sensitive data:
CREATE VIEW v_customer_directory AS
SELECT
id,
name,
LEFT(email, 3) || '***@' || SPLIT_PART(email, '@', 2) AS email,
city,
state,
-- Hide exact address
city || ', ' || state AS location
FROM customers;
-- Grant view access, deny table access:
REVOKE ALL ON customers FROM app_readonly;
GRANT SELECT ON v_customer_directory TO app_readonly;
-- Security barrier view (prevents optimi
...๐ Recursive Views
Recursive views encapsulate recursive CTEs so that users can query hierarchies with a simple SELECT โ no CTE knowledge required.
Recursive Views
Org charts and category trees as queryable views
-- RECURSIVE VIEWS: encapsulate recursive CTEs
CREATE RECURSIVE VIEW v_org_chart (
employee_id, employee_name, manager_id, depth, path
) AS
-- Anchor: CEO (no manager)
SELECT id, name, manager_id, 0,
ARRAY[name::text]
FROM employees WHERE manager_id IS NULL
UNION ALL
-- Recursive: employees under each manager
SELECT e.id, e.name, e.manager_id, oc.depth + 1,
oc.path || e.name::text
FROM employees e
JOIN v_org_chart oc ON e.manager_id = oc.employee_id;
-- Now anyone can query
...๐ INSTEAD OF Triggers
Complex views joining multiple tables aren't auto-updatable. INSTEAD OF triggers intercept INSERT/UPDATE/DELETE on the view and execute custom logic against the underlying tables.
โ ๏ธ Common Mistake
Forgetting to handle all DML operations. If you create an INSTEAD OF INSERT trigger but not UPDATE or DELETE, those operations silently do nothing. Always implement all three or raise an error for unsupported ones.
INSTEAD OF Triggers
Make multi-table views updatable
-- INSTEAD OF triggers: make complex views updatable
-- View joining multiple tables (not auto-updatable):
CREATE VIEW v_order_details AS
SELECT o.id AS order_id, o.order_date, o.status,
c.name AS customer_name, c.email,
oi.product_id, p.name AS product_name,
oi.quantity, oi.unit_price
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id;
-- INSTEAD OF trigger handles INSERT on the view:
CR
...๐ API Views
Use views as a stable API layer between your application and database. Version them for backward compatibility, and encapsulate complex logic so applications just do simple SELECTs.
API Layer Views
Versioned views as stable application interfaces
-- API VIEWS: stable interface for applications
-- Version your views to avoid breaking changes:
CREATE VIEW api.v1_products AS
SELECT id, name, price, category, status
FROM products
WHERE status = 'active';
CREATE VIEW api.v2_products AS
SELECT id, name, price, category, status,
description, -- new in v2
created_at, -- new in v2
COALESCE(discount_pct, 0) AS discount -- renamed in v2
FROM products
WHERE status = 'active';
-- Old applications us
...๐ Quick Reference
| View Type | Use Case |
|---|---|
| Simple view | Auto-updatable, single table |
| Security barrier | Prevent optimizer data leaks |
| Recursive view | Hierarchy queries for non-experts |
| INSTEAD OF | Custom DML on complex views |
| API view | Stable versioned interface |
๐ Lesson Complete!
You've mastered advanced view patterns for security, reuse, and API design. Next, explore analytical SQL for business intelligence!
Sign up for free to track which lessons you've completed and get learning reminders.