DUCKDB_TABLES() Examples

The duckdb_tables() function is a system function in DuckDB that provides useful metadata about all tables in your database. The function returns a table containing information about each table, including its schema, name, and various properties. It can be quite a handy tool for database introspection and management.

Syntax

The duckdb_tables() function is a table function and so you need to call it in a FROM clause, like this:

SELECT * FROM duckdb_tables();

Setting Up Example Tables

Let’s create some sample tables to demonstrate the duckdb_tables() function:

-- Create a simple customers table
CREATE TABLE customers (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Create an orders table with foreign key
CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER,
    order_date DATE NOT NULL,
    total_amount DECIMAL(10,2) CHECK (total_amount > 0),
    status VARCHAR(20) DEFAULT 'pending',
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

-- Create a temporary table
CREATE TEMPORARY TABLE temp_analytics (
    metric_name VARCHAR(50),
    metric_value DOUBLE,
    recorded_at TIMESTAMP
);

-- Create a table in a custom schema
CREATE SCHEMA inventory;
CREATE TABLE inventory.products (
    product_id INTEGER PRIMARY KEY,
    product_name VARCHAR(200) NOT NULL,
    price DECIMAL(8,2),
    stock_quantity INTEGER DEFAULT 0
);

-- Insert some sample data
INSERT INTO customers VALUES 
    (1, 'Blake Briskley', '[email protected]', '2025-01-15'),
    (2, 'Amy Toggle', '[email protected]', '2025-01-16'),
    (3, 'Fitch Flex', '[email protected]', '2025-01-17');

INSERT INTO orders VALUES 
    (101, 1, '2025-01-20', 250.00, 'completed'),
    (102, 2, '2025-01-21', 150.75, 'pending'),
    (103, 1, '2025-01-22', 89.99, 'shipped');

INSERT INTO temp_analytics VALUES 
    ('daily_sales', 490.74, '2025-01-22'),
    ('active_users', 3, '2025-01-22');

INSERT INTO inventory.products VALUES 
    (1, 'Laptop', 999.99, 50),
    (2, 'Mouse', 29.99, 200),
    (3, 'Keyboard', 79.99, 100);

List All Tables

Let’s start by listing all columns for all tables:

SELECT * FROM duckdb_tables()
ORDER BY schema_name, table_name;

Output:

+---------------+--------------+-------------+------------+----------------+-----------+---------+------+----------+-----------+-----------------+----------------+--------------+-------------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| database_name | database_oid | schema_name | schema_oid | table_name | table_oid | comment | tags | internal | temporary | has_primary_key | estimated_size | column_count | index_count | check_constraint_count | sql |
+---------------+--------------+-------------+------------+----------------+-----------+---------+------+----------+-----------+-----------------+----------------+--------------+-------------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| memory | 570 | inventory | 2053 | products | 2055 | NULL | {} | false | false | true | 3 | 4 | 1 | 0 | CREATE TABLE inventory.products(product_id INTEGER PRIMARY KEY, product_name VARCHAR NOT NULL, price DECIMAL(8,2), stock_quantity INTEGER DEFAULT(0)); |
| memory | 570 | main | 572 | customers | 2033 | NULL | {} | false | false | true | 3 | 4 | 2 | 0 | CREATE TABLE customers(id INTEGER PRIMARY KEY, "name" VARCHAR NOT NULL, email VARCHAR UNIQUE, created_at TIMESTAMP DEFAULT(CURRENT_TIMESTAMP)); |
| memory | 570 | main | 572 | orders | 2032 | NULL | {} | false | false | true | 3 | 5 | 2 | 1 | CREATE TABLE orders(order_id INTEGER PRIMARY KEY, customer_id INTEGER, order_date DATE NOT NULL, total_amount DECIMAL(10,2), status VARCHAR DEFAULT('pending'), CHECK((total_amount > 0)), FOREIGN KEY (customer_id) REFERENCES customers(id)); |
| temp | 1970 | main | 1971 | temp_analytics | 2047 | NULL | {} | false | true | false | 2 | 3 | 0 | 0 | CREATE TABLE temp_analytics(metric_name VARCHAR, metric_value DOUBLE, recorded_at TIMESTAMP); |
+---------------+--------------+-------------+------------+----------------+-----------+---------+------+----------+-----------+-----------------+----------------+--------------+-------------+------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

It returns quite a few columns. We can always refine the list to just a handful of columns that are usually of interest:

SELECT 
    database_name,
    schema_name,
    table_name,
    temporary,
    has_primary_key,
    column_count
FROM duckdb_tables()
ORDER BY schema_name, table_name;

Output:

+---------------+-------------+----------------+-----------+-----------------+--------------+
| database_name | schema_name | table_name | temporary | has_primary_key | column_count |
+---------------+-------------+----------------+-----------+-----------------+--------------+
| memory | inventory | products | false | true | 4 |
| memory | main | customers | false | true | 4 |
| memory | main | orders | false | true | 5 |
| temp | main | temp_analytics | true | false | 3 |
+---------------+-------------+----------------+-----------+-----------------+--------------+

Find Tables with Primary Keys

The function returns a has_primary_key column that returns a Boolean value. It returns true if this table object defines a primary key.

Therefore we can use duckdb_tables() to find all tables that have primary keys:

SELECT 
    schema_name,
    table_name,
    column_count,
    index_count
FROM duckdb_tables()
WHERE has_primary_key = true
ORDER BY table_name;

Output:

+-------------+------------+--------------+-------------+
| schema_name | table_name | column_count | index_count |
+-------------+------------+--------------+-------------+
| main | customers | 4 | 2 |
| main | orders | 5 | 2 |
| inventory | products | 4 | 1 |
+-------------+------------+--------------+-------------+

Identify Temporary Tables

We can also use the function to find out if there are any temporary tables. That’s because it returns a temporary column (which is a Boolean value indicating whether this is a temporary table). So we can search for all tables that contain true in their temporary column:

SELECT 
    table_name,
    column_count,
    estimated_size
FROM duckdb_tables()
WHERE temporary = true;

Output:

+----------------+--------------+----------------+
| table_name | column_count | estimated_size |
+----------------+--------------+----------------+
| temp_analytics | 3 | 2 |
+----------------+--------------+----------------+

Tables by Schema

We can use the function to find out how many tables are in each schema:

SELECT 
    schema_name,
    COUNT(*) as table_count,
    SUM(column_count) as total_columns
FROM duckdb_tables()
WHERE internal = false
GROUP BY schema_name
ORDER BY schema_name;

Output:

+-------------+-------------+---------------+
| schema_name | table_count | total_columns |
+-------------+-------------+---------------+
| inventory | 1 | 4 |
| main | 3 | 12 |
+-------------+-------------+---------------+

Table Size Analysis

We can analyze table size as well as other factors, such as column count, index count, etc:

SELECT 
    table_name,
    estimated_size,
    column_count,
    index_count,
    check_constraint_count,
    CASE 
        WHEN estimated_size > 1000000 THEN 'Large'
        WHEN estimated_size > 100000 THEN 'Medium'
        ELSE 'Small'
    END as size_category
FROM duckdb_tables()
WHERE internal = false
ORDER BY estimated_size DESC;

Output:

+----------------+----------------+--------------+-------------+------------------------+---------------+
| table_name | estimated_size | column_count | index_count | check_constraint_count | size_category |
+----------------+----------------+--------------+-------------+------------------------+---------------+
| products | 3 | 4 | 1 | 0 | Small |
| customers | 3 | 4 | 2 | 0 | Small |
| orders | 3 | 5 | 2 | 1 | Small |
| temp_analytics | 2 | 3 | 0 | 0 | Small |
+----------------+----------------+--------------+-------------+------------------------+---------------+

Find Tables with Constraints

This query returns tables with check constraints:

SELECT 
    schema_name,
    table_name,
    check_constraint_count,
    has_primary_key
FROM duckdb_tables()
WHERE check_constraint_count > 0
ORDER BY check_constraint_count DESC;

Output:

+-------------+------------+------------------------+-----------------+
| schema_name | table_name | check_constraint_count | has_primary_key |
+-------------+------------+------------------------+-----------------+
| main | orders | 1 | true |
+-------------+------------+------------------------+-----------------+

We only have one table with a check constraint. This is also shown in our previous query, which included a column showing how many check constraints each table has (only the orders table had 1 in this column – all other tables had 0).

Database Schema Overview

Let’s get a basic overview of the database structure:

SELECT 
    schema_name,
    table_name,
    column_count,
    has_primary_key,
    index_count,
    temporary,
    ROUND(estimated_size / 1024.0, 2) as size_kb
FROM duckdb_tables()
WHERE internal = false
ORDER BY schema_name, table_name;

Output:

+-------------+----------------+--------------+-----------------+-------------+-----------+---------+
| schema_name | table_name | column_count | has_primary_key | index_count | temporary | size_kb |
+-------------+----------------+--------------+-----------------+-------------+-----------+---------+
| inventory | products | 4 | true | 1 | false | 0.0 |
| main | customers | 4 | true | 2 | false | 0.0 |
| main | orders | 5 | true | 2 | false | 0.0 |
| main | temp_analytics | 3 | false | 0 | true | 0.0 |
+-------------+----------------+--------------+-----------------+-------------+-----------+---------+

Table Creation SQL

The sql column contains the SQL code required to recreate the table. So we can use this column to generate scripts to do just that:

SELECT 
    table_name,
    sql
FROM duckdb_tables()
WHERE table_name IN ('customers', 'orders')
ORDER BY table_name;

Output:

+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| table_name | sql |
+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| customers | CREATE TABLE customers(id INTEGER PRIMARY KEY, "name" VARCHAR NOT NULL, email VARCHAR UNIQUE, created_at TIMESTAMP DEFAULT(CURRENT_TIMESTAMP)); |
| orders | CREATE TABLE orders(order_id INTEGER PRIMARY KEY, customer_id INTEGER, order_date DATE NOT NULL, total_amount DECIMAL(10,2), status VARCHAR DEFAULT('pending'), CHECK((total_amount > 0)), FOREIGN KEY (customer_id) REFERENCES customers(id)); |
+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Database Health Check

We can use duckdb_tables() to provide a simple aggregate report of the database structure:

SELECT 
    COUNT(*) as total_tables,
    COUNT(CASE WHEN has_primary_key THEN 1 END) as tables_with_pk,
    COUNT(CASE WHEN temporary THEN 1 END) as temporary_tables,
    AVG(column_count) as avg_columns_per_table,
    SUM(estimated_size) as total_estimated_size
FROM duckdb_tables()
WHERE internal = false;

Output:

+--------------+----------------+------------------+-----------------------+----------------------+
| total_tables | tables_with_pk | temporary_tables | avg_columns_per_table | total_estimated_size |
+--------------+----------------+------------------+-----------------------+----------------------+
| 4 | 3 | 1 | 4.0 | 11 |
+--------------+----------------+------------------+-----------------------+----------------------+