What you'll practice: creating schemas, listing them, using
search_path, qualifying table names with a schema, moving tables between schemas, and dropping schemas cleanly.
A schema in PostgreSQL is a named namespace that owns tables, views, functions, and other objects. Every database has at least one schema called public, and every table lives inside a schema. Schemas are how you organise a database that has a lot going on — for example, a real app might put its sales tables under sales, its HR tables under hr, and its auth tables under auth.
- Create a schema with
CREATE SCHEMA - List all schemas in a database with
\dn - Reference a table using its fully qualified name (
schema.table) - Change the
search_pathso you can use unqualified names - Move a table from one schema to another
- Drop a schema (with and without
CASCADE)
From your terminal:
psql -U postgres -d sql_exercise -f 04-advanced-databases/02-schemas/schema.sql
psql -U postgres -d sql_exerciseThe first command seeds a tiny employee table inside a schema called adv_schemas. Then the second command drops you into psql.
There is no separate seed.sql for this section — the reference schema already inserts a few rows.
Use a psql meta-command to list every schema that already exists in sql_exercise.
Hint: the meta-command starts with \d.
Question: which of the schemas you see are system schemas (shipped with PostgreSQL) and which are user schemas?
Create two new schemas called adv_schemas_sales and adv_schemas_hr. They should be empty.
Verify: \dn should now list both schemas.
Without changing your search_path, run a SELECT against the employee table that lives inside adv_schemas.
Hint: the syntax is schema_name.table_name.
Expected: three rows — Ada, Alan, Grace.
Now try:
SELECT * FROM employee;Question: what error do you get, and why? Which schemas is PostgreSQL looking in right now? (Hint: SHOW search_path;)
Set your session's search_path so you can query employee without the schema prefix.
Verify: after setting it, SELECT * FROM employee; should now work.
Question: if you quit psql and reconnect, does the search_path persist?
Set search_path to adv_schemas_hr, adv_schemas (note the order). Then run SELECT * FROM employee;.
Question: which schema did the query find employee in, and why?
Create a department table inside adv_schemas_hr with columns:
| Column | Type |
|---|---|
id |
integer |
name |
varchar(100) not null |
head_count |
integer not null |
There are two ways to do this — using a qualified name in the CREATE TABLE, or by first switching schema. Try both in theory, do it whichever way you prefer.
Verify: \dt adv_schemas_hr.*
Insert two rows into adv_schemas_hr.department:
| id | name | head_count |
|---|---|---|
| 1 | Engineering | 42 |
| 2 | People | 8 |
Verify: SELECT * FROM adv_schemas_hr.department; should return both rows.
Move the employee table from adv_schemas into adv_schemas_hr, so that both employee and department live in the HR schema.
Hint: ALTER TABLE ... SET SCHEMA ...
Verify:
\dt adv_schemas_hr.*should list both tables.\dt adv_schemas.*should now be empty.
In this contrived example, assume each employee belongs to department 1. Write a query that joins adv_schemas_hr.employee and adv_schemas_hr.department so you see each employee with their department name.
You'll need to join them on a literal value since there's no department_id column yet — use ON d.id = 1 to fake it.
Expected: three rows with both an employee name and Engineering.
Drop the adv_schemas_sales schema. It's empty, so no CASCADE is needed.
Verify: \dn
Try to drop adv_schemas_hr without CASCADE. Watch it fail. Then drop it with CASCADE.
Question: why does PostgreSQL make you opt in to CASCADE instead of just doing it for you?
From your terminal, re-run:
psql -U postgres -d sql_exercise -f 04-advanced-databases/02-schemas/schema.sqlQuestion: why can you re-run this script safely? What line at the top makes it idempotent?
Read \h ALTER ROLE in psql. Can you make it so that every time the postgres user connects to sql_exercise, their search_path already includes adv_schemas?
Write a query against information_schema.schemata that lists every non-system schema. (Hint: system schemas start with pg_ or are called information_schema.)
Every schema has an owner. Look up who owns adv_schemas using \dn+. What is the difference between \dn and \dn+?
DROP SCHEMA IF EXISTS adv_schemas CASCADE;
DROP SCHEMA IF EXISTS adv_schemas_hr CASCADE;
DROP SCHEMA IF EXISTS adv_schemas_sales CASCADE;