Intermediate SQL
Views
Why Views?
Not everyone should see the full picture. Suppose someone needs an instructor’s name and department, but not their salary. Instead of trusting every query to remember to hide the salary column, define it once:
A view is exactly this — a saved query that behaves like a “virtual table.” It’s not real stored data; it’s a name attached to a query expression that gets substituted in wherever the view is used.
Creating a View
-- A view of instructors without salary
create or replace view faculty as
select id, name, dept_name from instructor;
-- Use it like a table
select name from faculty where dept_name = 'Biology';
-- A view with computed columns
create or replace view dept_salary_totals(dept_name, total_salary) as
select dept_name, sum(salary)
from instructor
group by dept_name;
select * from dept_salary_totals;Views Built on Views
Views can refer other views, not just base tables:
create or replace view physics_fall_2006 as
select course.course_id, sec_id, building, room_number
from course, section
where course.course_id = section.course_id
and course.dept_name = 'Physics'
and section.semester = 'Fall'
and section.year = 2006;
create or replace view physics_fall_2006_taylor as
select course_id, room_number
from physics_fall_2006
where building = 'Taylor';
select * from physics_fall_2006_taylor;v1depends directly onv2ifv2appears inv1’s definition.v1depends onv2if there’s any chain of dependency leading fromv1tov2.- A view is recursive if it depends on itself.
View Expansion
To understand what a nested view really means, SQL conceptually expands it: repeatedly replace each view name with its underlying query, until only base tables remain.
repeat:
find a view reference v_i in the expression
replace v_i with v_i's defining query
until no view references remain
This terminates as long as no view is recursive.
Updating Views
Inserting into a view secretly inserts into the underlying table:
But this only works cleanly for simple views — most databases require:
fromclause has exactly one base tableselectlists plain column names only (no expressions, aggregates, ordistinct)- Any column left out of the
selectcan default tonull - No
group byorhaving
If a view joins multiple tables, an insert can be ambiguous (e.g., which department’s row gets the new value if several departments share a building?) — and some inserts that satisfy the view’s output can violate its where clause entirely, making the update meaningless to translate.
Try inserting into the dept_salary_totals view (it has group by), and the database refuses:
Materialized Views
A materialized view physically stores the view’s result as a real table, instead of recomputing it on every query — faster to read, but it goes stale the moment the underlying tables change. Keeping it correct requires actively maintaining it (refreshing it) whenever the source data updates.
| View Type | Storage | Speed | Freshness |
|---|---|---|---|
| Regular View | None (just a saved query) | Recomputed every time | Always current |
| Materialized View | Physical table | Fast to read | Needs manual/scheduled refresh |
begin;
create materialized view dept_count as
select dept_name, count(*) as num_instructors
from instructor group by dept_name;
insert into instructor values ('30765', 'Green', 'Biology', 72000);
select * from dept_count where dept_name = 'Biology'; -- still the old count
refresh materialized view dept_count;
select * from dept_count where dept_name = 'Biology'; -- now updated
rollback;Transactions
A transaction is a unit of work — a group of one or more SQL statements that should succeed or fail as a whole.
- Atomic: either the entire transaction completes, or none of it does (rolled back as if it never happened).
- Isolated: a transaction shouldn’t see the messy, half-finished state of another transaction running at the same time.
- Transactions begin implicitly, and end with either:
commit work— make all changes permanentrollback work— undo everything since the transaction began
Integrity Constraints
Constraints stop valid-looking but wrong changes from silently corrupting the database — e.g., “an instructor’s salary must be greater than 29000” or “every instructor must have a name.”
On a Single Relation
| Constraint | Purpose |
|---|---|
not null |
Column can never be empty |
primary key |
Uniquely identifies each row; implies not null + unique |
unique (A1, ..., Am) |
Columns form a candidate key — but unlike primary keys, candidate keys can be null |
check(P) |
Row must satisfy predicate P |
The university dataset already has these constraints. Each insert below breaks one of them, so the database rejects it:
Creating a new table with a check constraint (in the practice workspace):
A value outside the list is rejected by the check:
Referential Integrity
Ensures a value referenced in one table actually exists in the table it points to. If 'Biology' appears as a department name in instructor, there must be a matching row for 'Biology' in department.
Formal definition: If
Ais the primary key of relationS, thenAis a foreign key of relationRif every value ofAappearing inRalso appears inS.
Cascading Actions
What should happen when a referenced row is deleted or updated?
drop table if exists project_cascade;
drop table if exists project;
drop table if exists department;
create table department (
dept_name varchar(20) primary key,
building varchar(15)
);
create table project (
project_id char(5) primary key,
title varchar(20),
dept_name varchar(20) references department
);
-- with explicit cascading behavior
create table project_cascade (
project_id char(5) primary key,
title varchar(20),
dept_name varchar(20),
foreign key (dept_name) references department
on delete cascade
on update cascade
);| Action | Effect when referenced row is deleted/updated |
|---|---|
cascade |
Automatically delete/update matching rows too |
no action |
Block the delete/update if references exist (default) |
set null |
Set the foreign key to null |
set default |
Set the foreign key to its default value |
The university dataset uses these actions. Deleting a department sets its instructors’ dept_name to null (set null), and deleting a student removes their takes rows (cascade):
The Chicken-and-Egg Problem
Self-referencing constraints can deadlock a single insert. Consider:
How do you insert the very first employee if their manager must already exist? Three options:
- Insert the manager before the employee who reports to them.
- Set
manager_idtonullinitially, thenupdateit later (impossible if the column isnot null). - Defer constraint checking until the transaction commits (covered in advanced topics).
SQL Data Types and Schemas
Built-in Date/Time Types
| Type | Stores | Example |
|---|---|---|
date |
Year, month, day | date '2025-7-27' |
time |
Hours, minutes, seconds | time '09:00:30' |
timestamp |
Date + time together | timestamp '2025-7-27 09:00:30.75' |
interval |
A span of time | interval '1' day |
Indexes
An index is a shortcut structure that lets the database jump straight to matching rows instead of scanning the whole table.
User-Defined Types (UDT)
Like a typedef in C — gives a meaningful name to an existing type.
Domains
Similar to UDTs, but domains can carry their own constraints.
drop table if exists visitor;
drop domain if exists person_name;
drop domain if exists member_kind;
create domain person_name varchar(20) not null;
create domain member_kind varchar(10)
constraint member_kind_test
check (value in ('Student', 'Faculty', 'Staff'));
create table visitor (name person_name, kind member_kind);
insert into visitor values ('Asha', 'Student'); -- accepted
select * from visitor;A value the domain does not allow is rejected:
Large Object Types
For storing big, unstructured content like photos, videos, or files:
| Type | Stores |
|---|---|
blob |
Binary Large OBject — raw binary data |
clob |
Character Large OBject — large character data |