From Messy JOINs to Clean Architecture: My SQL Views Wake-Up Call
- Prajna M
- Jan 14
- 6 min read
It was 3 PM on a Tuesday when Rashmi, my lead, messaged me:
Can you send the top rented movies report again , but only for Store 1?
I already knew what that meant.
A long SQL query
Multiple JOINs across film, inventory, rental, and payment
Copy, paste, tweak, run, export
Five minutes later, another message : Oh exclude staff test rentals too.
That's when it hit me. I wasn't analyzing data anymore. I was rewriting the same logic again and again.
There had to be a better way. That's when I truly understood SQL Views.
What Is a SQL View?
A view is a database object created from a SQL query.
It does not store data
It stores query logic
It behaves like a virtual table
Every time you query a view, the database re-runs the underlying SQL.

Think of a view as:
A reusable window into your data — not a copy of it.
My First "Aha" Moment: Hiding Complexity
The film table in the DVD Rental database contains many columns.
But Operations only needed:
film_id
title
So I created a simple view. And get only what they need. No clutter. No confusion.

Note: I'm using the PostgreSQL DVD Rental sample database for these examples. It contains tables like film, rental, payment, and inventory , a perfect playground for learning Views. The patterns I show here apply to any real-world database : e-commerce, SaaS, fintech, healthcare.
Lesson #1: Views are not shortcuts - they are boundaries.
Why This Matters in Real Projects
This pattern is commonly used in production environments for:
Marketing teams - Showing customer lists without credit card data.
Partner integrations - Providing API access to product catalogs without revealing wholesale pricing.
Analytics dashboards - Displaying aggregated metrics without exposing raw transaction logs.
Why CREATE OR REPLACE Feels So Strict
When I tried modifying the view, PostgreSQL stopped me.
Error: You can't rename columns

Error: You can't change data types

Error: You can't change column order

Error: You can't insert columns in the middle

Only this works: Added column at end

Why so many rules?
Because views act like Contracts. Dashboards, reports, and jobs already depend on them. Breaking a view doesn't just affect one query , it creates a cascade of failures.
The domino effect:
A reporting tool has been querying your view for 6 months. Business intelligence dashboards refresh hourly. ETL pipelines run nightly. Analytics exports feed into executive reports.
Then you casually rename 'title' to 'movie_title'.
What happens next:
Dashboards display error messages instead of data
Scheduled reports fail to generate
API endpoints return 500 errors
Business decisions get delayed
Your team spends hours debugging production
So , PostgreSQL prevents this by being strict about view modifications.
Lesson #2: Stability is the feature.
ALTER VIEW: Safe Changes Without Breaking Tables
When change is unavoidable, ALTER VIEW is the right tool.

Below Screenshot showing renamed column in view. While the original table stays untouched:

You can even rename the view itself:

Views Track Data Changes, Not Schema Changes
This behavior confused me at first - until I tested it.
Step 1: Create the View
CREATE VIEW expensive_rents AS
SELECT *
FROM payment
WHERE amount >= 11.99;
Step 2: Change the Table Structure
ALTER TABLE payment
ADD COLUMN promo_code VARCHAR(20);
Query the base table:

Screenshot showing new promo_code column in payment table.
But when you run the view:

Screenshot showing view WITHOUT promo_code column. The new column does not appear in the view.
Why? Because the view stores the query as it was written. Since we used SELECT *, it captured the columns that existed at creation time.
Step 3: Insert New Data In Payment Table & Run expensive_rents View

Screenshot showing insert statement and the new row appears in the view immediately.
Lesson #3: Views reflect data changes, not table structure changes.
WITH CHECK OPTION: Views That Enforce Rules
I needed a view that showed only rentals handled by staff_id = 1 .So created view staff1_view .
CREATE VIEW staff1_view AS
SELECT *
FROM rental
WHERE staff_id = 1
WITH CHECK OPTION;
Now the database enforces the rule. Trying to insert staff_id = 2 through the staff1_view.
INSERT INTO staff1_view (rental_date , inventory_id, customer_id , staff_id)
VALUES ('2024-01-15', 100, 50, 2); -- Wrong staff_id!

Screenshot showing WITH CHECK OPTION blocking invalid insert
Lesson #4: Views can enforce business rules, not just display data.
Updatable Views: When Can You UPDATE a View?
This surprised me.
Rule 1: Single-table views are updatable

Because this view is built from a single table, PostgreSQL can safely map every row in the view back to exactly one row in the payment table.
Rule 2: DISTINCT makes views read-only

When a view uses DISTINCT, PostgreSQL cannot determine which underlying row should be updated.
Rule 3: GROUP BY makes views read-only
Views with GROUP BY return aggregated results, not actual table rows. Since there is no direct row-to-row mapping, PostgreSQL does not allow updates. To update data, you must update the base table directly or use INSTEAD OF triggers on the view to manually define how updates should be applied.
Summary: Updatable vs Read-Only Views
Updatable IF: | NOT Updatable IF: |
Single table No DISTINCT No GROUP BY No window functions | Multiple tables (JOINs) Uses DISTINCT Uses GROUP BY or aggregates Uses UNION, EXCEPT, etc. |
Lesson #5: If a view aggregates or deduplicates data, it becomes read-only.
Views as a Security Layer (Real Scenario)
Finance needed revenue numbers, but not:
customer PII
staff data
raw payment records
So I created a controlled view:

Then I granted access only to the view:
GRANT SELECT ON film_revenue_view TO finance_team;
No customer names
No payment details
No sensitive columns
Just what they need
Lesson #6: Views are one of the cleanest security tools in SQL.
Real-World Security Examples
This is where views stopped being a “SQL feature” and started feeling practical.
Healthcare systems: Product teams see appointment counts, not patient diagnoses.
Fintech platforms: Business teams tracked daily transaction volumes and revenue trends, but never looked at account numbers or payment details. Views masked sensitive financial data.
E-commerce operations: Warehouse staff see order items, not customer addresses.
Analytics dashboards: The dashboards presented consolidated data in the form of totals and averages, without exposing user-level actions.
In every case, the pattern was the same:
Give teams exactly what they need — and nothing they shouldn’t see.
Views vs Materialized Views (Performance Reality)

Every dashboard refresh re-ran this expensive query across thousands of rows.
Load time: 51 msec.
The Game Changer: Materialized Views
So I switched to a materialized view:

Load time : 40 msec.
Key Difference:

After inserting new data, the materialized view stayed stale until:
REFRESH MATERIALIZED VIEW top_customers_matview;
Lesson #7: Views = live data Materialized views = speed
Common Mistakes I Made (So You Don't Have To)
Mistake 1: Using SELECT * in Views
Problem: When table structure changes, views can break silently or show unexpected columns.
Fix: Always specify columns explicitly:
-- Bad
CREATE VIEW my_view AS
SELECT * FROM payment;
-- Good
CREATE VIEW my_view AS
SELECT payment_id, customer_id, amount, payment_date
FROM payment;
Mistake 2: Forgetting to Refresh Materialized Views
Schedule automated refresh jobs (hourly, nightly, or weekly).
Mistake 3: Giving View Access Without Testing
Problem: Users can still see sensitive data through unexpected columns or JOINs.
Fix: Always test with a restricted user account before granting access:
-- Create test user
CREATE USER test_finance WITH PASSWORD 'secure_password';
-- Grant view access
GRANT SELECT ON film_revenue_view TO test_finance;
-- Test by connecting as that user
-- psql -U test_finance -d dvdrental
The Big Realization
I used to think SQL views were just saved queries.
Now I see them as:
Abstraction layers — hiding complexity
Security boundaries — controlling access
Performance tools — through materialization
Architectural building blocks — designing systems
Once that clicks, SQL stops being about queries. It becomes design.
Your Turn
If you're learning SQL, here's my challenge:
1. Pick a table in your practice database
2. Create a view that hides 2-3 sensitive columns
3. Grant read-only access to that view
4. Try updating through the view (and watch what happens)
You'll learn more in 10 minutes than reading any tutorial.

