top of page

Welcome
to NumpyNinja Blogs

NumpyNinja: Blogs. Demystifying Tech,

One Blog at a Time.
Millions of views. 

From Messy JOINs to Clean Architecture: My SQL Views Wake-Up Call

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.





 
 

+1 (302) 200-8320

NumPy_Ninja_Logo (1).png

Numpy Ninja Inc. 8 The Grn Ste A Dover, DE 19901

© Copyright 2025 by Numpy Ninja Inc.

  • Twitter
  • LinkedIn
bottom of page