Skip to content
Reference / SQL Schemas
Reference

SQL Schemas

A schema is a folder for database objects. It keeps raw, staging, and reporting tables from colliding, and makes it obvious which is which.

A schema is a named group of objects inside a database. Tables, views, sequences, and macros all live in one. Two schemas can each hold a table called orders without conflict, because the full name of an object includes the schema it sits in.

Create one and put something in it
create schema reporting;

create table reporting.category_revenue as
select p.category_id,
       sum(oi.quantity * oi.unit_price) as revenue
from order_items oi
join products p on p.product_id = oi.product_id
group by p.category_id;

select * from reporting.category_revenue
order by revenue desc, category_id;

Objects you create without naming a schema go into main, which is where everything on the rest of this site lives.

Watch out

Creating a schema that already exists is an error, so use CREATE SCHEMA IF NOT EXISTS in any script you expect to run more than once.

Qualified names

A fully qualified name has three parts: database.schema.object. You will mostly write two, and often one.

Written as Means
category_revenue Look in the current schema.
reporting.category_revenue That schema, in the current database.
memory.reporting.category_revenue Leaves nothing to interpretation.

Query an object in another schema without qualifying it and DuckDB reports Table with name category_revenue does not exist!, usually with a Did you mean "reporting.category_revenue"? hint, which is the fastest way to spot that you forgot the prefix.

Switching schemas

USE changes which schema unqualified names resolve to, so you can stop typing the prefix while working inside one area.

Change the default
use reporting;

-- no prefix needed now
select * from category_revenue;

-- and back
use memory.main;

Tables in main stay reachable while you are in another schema, so USE adds a place to look rather than cutting you off from the rest of the database. select current_schema() tells you where you are.

Watch out

USE is a per-connection setting. In the SQLShed playground each Run is a new connection, so a USE from one Run does not carry into the next. Put it at the top of the same script that depends on it.

What to name them

The convention most teams land on describes how finished the data is, not who owns it:

Schema Holds
raw Loaded exactly as it arrived. Never edited by hand.
staging Cleaned and typed, not yet joined into anything.
mart / reporting What people query. Business names, joins already done.
sandbox Whatever someone is trying today. Droppable without asking.

The value is that the schema name answers "can I trust this table?" before anyone opens it.

Schemas vs databases

A database contains schemas; a schema contains objects. One query can join across schemas in the same database without ceremony. Crossing databases means attaching the other one first, and in DuckDB that is a separate ATTACH step. If you are deciding between the two, schemas are almost always what you want: they are lighter, and joins stay simple.

Listing what exists

Schemas, and what is in them
select schema_name
from duckdb_schemas()
where not internal
order by schema_name;

select table_schema, table_name
from information_schema.tables
where table_schema not in ('information_schema')
order by table_schema, table_name;

Cleaning up

Dropping a schema that still contains objects fails with a Dependency Error. CASCADE removes the schema and everything inside it, which makes a named schema the tidiest possible sandbox: do the work in it, drop it in one statement, forget nothing.

One statement to clear the bench
drop schema sandbox cascade

See DROP statements for what CASCADE does and does not reach. You cannot drop main; DuckDB reports it as an internal system entry.

Dialect note

Schemas are standard, but the layer above them is not. PostgreSQL matches DuckDB closely in that databases contain schemas, but a PostgreSQL query cannot reach another database at all without an extension such as postgres_fdw, where DuckDB joins across anything you ATTACH. MySQL treats "schema" and "database" as the same thing, so there is no nesting at all. SQL Server puts schemas inside databases inside a server. BigQuery calls the same idea a dataset. The two-part schema.table name is the portable part.