SQL SHOW TABLES and DESCRIBE
Before you write a query, ask the database what it holds: which tables exist, what columns they have, and what type each column really is.
Every database keeps a catalog: a record of its own tables,
views, columns, and types. You can query it like any other data. That makes
the first step with an unfamiliar database a question, not a guess: which
tables are there, what is in them, and is price a number or
text?
show tables
On Garden Shop this returns six names, from categories to
suppliers, plus anything you have created this session. Views
are listed alongside tables, with nothing to tell them apart.
The columns of one table
describe orders
One row per column: its name, its type, whether it allows nulls
(null), and whether it is part of a key (key).
Garden Shop's tables are loaded from CSV files without constraints, so every
column says YES to nulls and the key column is
empty. SHOW orders returns the same thing.
DESCRIBE shows what the engine stored, not what a diagram or a
column name suggests. Garden Shop's price comes back as
DOUBLE, and its IDs as BIGINT, because DuckDB
inferred the types while reading the CSV files. When a type matters for a
cast, a join, or a rounding question, check it here first.
A table built with constraints fills in the other columns:
create or replace table order_imports (
order_id integer primary key,
customer_id integer not null,
status varchar default 'new'
);
describe order_imports; order_id shows PRI under key, both ID
columns say NO to nulls, and status shows its
default, 'new'. See
CREATE TABLE and
table constraints.
Describe a query before you run it
describe
select o.order_id, sum(oi.quantity * oi.unit_price) as revenue
from orders o
join order_items oi on oi.order_id = o.order_id
group by o.order_id DESCRIBE also takes a whole query and returns the columns it
would produce, without the rows. revenue comes out as
DOUBLE. That is the quickest way to check what a
CREATE TABLE AS SELECT or a
view will be built with.
Profile every column at once
summarize customers SUMMARIZE is DuckDB's one-statement profile: per column, the
type, minimum and maximum, an approximate count of distinct values, the
average and quartiles for numbers, the row count, and the percentage of
nulls. On customers it shows straight away that a quarter of
the phone numbers are missing. It takes a query as well as a table name.
approx_unique is an estimate, and on small tables it can be
wrong in both directions. On customers it reports 21 distinct
customer_id values in a 20-row table. Use
count(distinct customer_id) when the number matters; see
profile a table.
Tables in other schemas
create schema if not exists reporting;
create or replace table reporting.monthly_revenue as
select date_trunc('month', o.order_date) as month,
sum(oi.quantity * oi.unit_price) as revenue
from orders o
join order_items oi on oi.order_id = o.order_id
group by all;
show tables; monthly_revenue is not in the list. SHOW TABLES
covers the schema you are working in, which is main unless you
changed it. Ask for the other schema by name, or for everything:
show tables from reporting show all tables SHOW ALL TABLES returns one row per table or view with its
database, schema, column names, and column types. DESCRIBE
needs the schema too: describe reporting.monthly_revenue. See
schemas.
show databases;
select current_database(), current_schema();
The playground runs one in-memory database, named memory, with
the current schema set to main.
Querying the catalog
SHOW and DESCRIBE are shortcuts. Underneath,
DuckDB exposes its catalog as table functions you can filter, join, and sort
like any table. Two questions they answer that the shortcuts cannot:
select table_name, column_name, data_type
from duckdb_columns()
where column_name = 'customer_id'
order by table_name A search across every table at once: the fastest way to find join keys in a database you have not seen before.
select schema_name, table_name, estimated_size, column_count
from duckdb_tables()
order by estimated_size desc, table_name estimated_size is DuckDB's row count estimate. It also has a
sql column holding the CREATE TABLE statement that
would rebuild the table.
| Function | One row per |
|---|---|
duckdb_tables() | table |
duckdb_columns() | column of a table or view |
duckdb_views() | view, with its stored sql |
duckdb_schemas() | schema |
duckdb_constraints() | constraint: primary key, unique, check, not null, foreign key |
duckdb_functions() | function overload |
duckdb_types() | data type, including aliases |
duckdb_indexes() | index |
Some of these return DuckDB's own objects too. duckdb_views()
lists 47 rows on a database with no views of yours, because DuckDB's own
information_schema and pg_catalog views are in it.
They are flagged internal. duckdb_functions()
lists over 2,600. Filter with where not internal, or by name.
create or replace view customer_revenue as
select o.customer_id, sum(oi.quantity * oi.unit_price) as revenue
from orders o
join order_items oi on oi.order_id = o.order_id
group by o.customer_id;
select view_name, sql
from duckdb_views()
where not internal; select distinct function_name, description
from duckdb_functions()
where function_name like 'date%'
order by function_name
A function with several overloads appears once per overload, which is why
the lookup needs distinct.
The portable version: information_schema
select column_name, data_type, is_nullable, column_default
from information_schema.columns
where table_schema = 'main'
and table_name = 'order_imports'
order by ordinal_position information_schema is the SQL standard's catalog: views named
tables, columns, views, and more,
with the same column names across engines. The query above runs on
PostgreSQL, MySQL, and SQL Server once main is swapped for
their schema or database name. Use it in scripts meant to move between
databases, and the duckdb_* functions when you want DuckDB's
extra detail.
select table_schema, table_name, table_type
from information_schema.tables
where table_schema in ('main', 'reporting')
order by table_schema, table_name table_type reads BASE TABLE or VIEW,
the difference SHOW TABLES leaves out.
What can go wrong
show columns from orders
That is MySQL. DuckDB answers
Parser Error: syntax error at or near "from". The commands for
listing tables differ more between databases than almost any other SQL, and
several of the best-known ones are not SQL at all: \d and
\dt belong to PostgreSQL's psql client,
.tables and .schema to the SQLite and DuckDB
command-line shells, and Oracle's DESC to SQL*Plus and SQLcl.
Typed into a SQL editor, including this playground, each one is a syntax
error.
describe custmers
DuckDB says Table with name custmers does not exist! and
suggests customers. The same error appears for a table in
another schema named without its schema, so if the suggestion has a prefix,
like reporting.monthly_revenue, use it. Every other cause of
this error is on
Catalog Error: table does not exist.
Across databases
| Engine | List tables | Columns of one table |
|---|---|---|
| DuckDB | show tables | describe t |
| PostgreSQL | information_schema.tables, or \dt in psql
| information_schema.columns, or \d t in
psql
|
| MySQL | show tables | describe t or show columns from t |
| SQL Server | information_schema.tables or sys.tables | information_schema.columns, or
exec sp_help 't' |
| Oracle | user_tables, all_tables | user_tab_columns, or desc t in SQL*Plus
|
| SQLite | sqlite_schema, or .tables in the shell
| pragma table_info(t) |
Oracle and SQLite have no information_schema; Oracle's catalog
is its data dictionary views, and SQLite's is the sqlite_schema
table. PostgreSQL's SHOW reads settings, not tables. DuckDB
accepts both the MySQL-style shortcuts and SQLite's
pragma table_info('orders'), so a habit from either mostly
carries over.
Related
Count rows, nulls, distinct values, and the date range in one query.
List the tables that exist, then check spelling and schema.
Pick a type for every column, and a default for the rest.
DROP TABLE will not remove a view, and IF EXISTS will not save it.
Find the text that won't cast, then clean it or use TRY_CAST.
Group objects into namespaces like staging and reporting.