DuckDB Cheat Sheet

DuckDB is an in-process analytics database that runs SQL straight over CSV, Parquet and JSON files. This sheet covers the CLI, the SQL you reach for daily, exports, ATTACH, extensions and the Python API.

Updated Oct 12, 2026Tested against DuckDB 1.5.6

Every example below uses one small shop: an file, an file, and a database holding and .

Reading Files

DuckDB queries files in place. A quoted path works anywhere a table name does.

CommandWhat it does
Query a file directly, format picked from the extension
Same query, FROM-first shorthand for
Read CSV with auto-detected delimiter, header and types (file not found?)
Name columns of a headerless file
Force a column type instead of the guess
Parse day-first dates
Sniff types from every row, not the first 20,480
Skip rows that fail to parse or cast
Show what the sniffer detected, as a reusable query
Combine files with different columns, tag the source file
Read many Parquet files as one table
Turn folders into columns
Inspect Parquet column types without reading data
Read JSON arrays or newline-delimited JSON
Read one sheet of an Excel file
Query S3 directly, needs httpfs and a secret
List the columns and types DuckDB inferred

Example: revenue by order status since September 1, queried straight from .

sql
SELECT status, count(*) AS orders, sum(amount) AS revenue
FROM read_csv('orders.csv')
WHERE created_at >= DATE '2026-09-01'
GROUP BY ALL
ORDER BY revenue DESC;

In 1bench: connect straight to a Parquet, CSV or JSON file at an S3 or HTTPS URL and query it as a table, with S3, MinIO and R2 credentials in the connection form. See the DuckDB GUI.

CLI Commands

Shell flags first, then the dot commands you type inside the prompt.

CommandWhat it does
Open a database file, creating it if missing
Start an in-memory session, nothing is saved
Open without write access, several readers can share the file
Run one statement and exit
Run a SQL file and exit
Open the local web UI in a browser (not loading?)
List tables and views with their columns
Show column names and types
Min, max, nulls and quantiles for every column
Print the CREATE statement
Run a SQL file inside the session
Switch output: , , , ,
Show more than the default 40 rows in duckbox mode
Re-print the previous result without truncation
Print run time after each query
Send only the next result to a file
List attached databases and their files
Leave the shell, as do and Ctrl+D
Cap memory, default is 80% of RAM (out of memory?)
Limit CPU threads
Choose where larger-than-memory work spills
Cut memory on big imports and exports
Read one setting; lists all

Example: export customers ranked by total spend to a CSV file with one shell command.

bash
duckdb shop.duckdb -csv -c "
  SELECT customer_id, sum(amount) AS total
  FROM orders GROUP BY ALL ORDER BY total DESC" > top_customers.csv

In 1bench: open and the sidebar lists every table and view with its row count, and each table's Schema tab shows its columns and types, so you skip and . See the DuckDB GUI.

Functions

The functions that come up in almost every query. The full list belongs on its own page.

FunctionExampleReturns
Joined string, NULLs skipped
as a list
First capture group
First instant of the month
as text
, , ,
Timestamp one week ago
Conditional count
Order ID of the largest order
One comma-separated string
, 90th percentile
1, 2, 3 per customer
Previous order amount
JSON field as text
or
New list, each value mapped
List comprehension
First non-NULL value

Example: order count, revenue and refunds per month from the table.

sql
SELECT strftime(date_trunc('month', created_at), '%Y-%m') AS month,
       count(*) AS orders,
       round(sum(amount), 2) AS revenue,
       count(*) FILTER (WHERE status = 'refunded') AS refunds
FROM orders
GROUP BY ALL
ORDER BY month;

Filtering and Sorting

CommandWhat it does
Match any value in a list
Drop rows with a missing value
Pattern match, case-sensitive
Pattern match, case-insensitive
Regular expression match
Inclusive range on both ends
Rows with no match in another table
Filter on an aggregate (not in WHERE)
Top N per group without a subquery
Sort with NULLs at the end
Sort by every column, left to right
Page through results
Random sample of a big table

Example: each customer's two largest non-refunded orders, using instead of a subquery.

sql
-- each customer's two largest orders
SELECT customer_id, order_id, amount
FROM orders
WHERE status <> 'refunded'
QUALIFY row_number() OVER (PARTITION BY customer_id ORDER BY amount DESC) <= 2
ORDER BY customer_id, amount DESC;

Tables and Writes

CommandWhat it does
Create and load a table from a file
Define a table by hand
DestructiveDrops the old table and its data first
Session-only table, gone on exit
Append rows from a file
Match columns by name, not position
Upsert on a key
Skip rows whose key exists
Update matches, insert the rest in one pass
Change rows in place
DestructiveRemove matching rows, all rows without WHERE
DestructiveEmpty the table
DestructiveRemove the table and its data
Add a column; and also work
Saved query, no stored data
Reusable SQL function
Group writes in one transaction, undoes

Example: load new orders into a temp table, then them to update statuses and add new rows.

sql
CREATE TEMP TABLE staging AS FROM 'orders_new.csv';

MERGE INTO orders o
USING staging s ON o.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET status = s.status
WHEN NOT MATCHED THEN INSERT BY NAME;

Joins and Reshaping

CommandWhat it does
Inner join on a shared column name
Keep every left row
Left rows with no match
Left rows with a match, no duplicates
Join to the latest earlier event
Group by every non-aggregated column
All columns but some
All columns, one rewritten
Apply one expression to columns matching a regex
Rows to columns
Columns to rows
One row per list element
Flatten a list of structs into columns
Stack queries, matching columns by name

Example: pivot orders into one row per month with a revenue column for each status.

sql
PIVOT (
  SELECT strftime(created_at, '%Y-%m') AS month, status, amount FROM orders
)
ON status USING sum(amount)
GROUP BY month
ORDER BY month;

Exporting

CommandWhat it does
DestructiveWrite Parquet, silently overwrites an existing file
Smaller Parquet files
Export a query result to CSV
Newline-delimited JSON; add for one array
Write Excel, run first
Write hive-partitioned folders
DestructiveClears the target folder before writing
Load a file into an existing table
Dump every table plus schema SQL
Restore an exported database

Example: write the orders table to Parquet folders partitioned by year and month.

sql
COPY (
  SELECT *, year(created_at) AS year, month(created_at) AS month
  FROM orders
) TO 'lake/orders' (FORMAT parquet, PARTITION_BY (year, month), OVERWRITE_OR_IGNORE);

In 1bench: export any table to CSV, JSON, NDJSON or SQL from a dialog, keeping your current filter, sort and visible columns, no needed. See the DuckDB GUI.

ATTACH

ATTACH opens another database file, or a live SQLite, PostgreSQL or MySQL database, next to the current one.

CommandWhat it does
Open a second DuckDB file, creating it if missing
No error if already attached
Attach without write access
Query a SQLite file as tables
Query a live PostgreSQL database
Query a live MySQL database
Qualify tables in other databases (catalog error?)
Make it the default for unqualified names
List attached databases
Copy every table across
Close it

Example: attach a live Postgres database read-only and join its customers to local Parquet events.

sql
ATTACH 'dbname=shop host=localhost user=app' AS pg (TYPE postgres, READ_ONLY);

-- join live Postgres customers to local Parquet events
SELECT c.email, count(*) AS events
FROM pg.public.customers c
JOIN 'events.parquet' e ON e.user_id = c.customer_id
GROUP BY ALL
ORDER BY events DESC;

Extensions

Core extensions autoload on first use. Install them ahead of time for offline machines, or by hand when autoload fails.

CommandWhat it does
List extensions, installed and loaded state
Install and load by hand (download failed?)
Install a community extension
Upgrade installed extensions
Read and write , , paths
and
GEOMETRY type, functions, GDAL file formats
and Iceberg catalogs
DuckLake lakehouse tables
, , ATTACH and scan those databases
Full-text search with BM25 ranking
HNSW indexes for vector similarity search
Use your AWS credentials for S3
Keep the secret across sessions

Example: count the last 30 days of S3 Parquet events by type, signed in with your AWS credentials.

sql
CREATE SECRET (TYPE s3, PROVIDER credential_chain);

SELECT event, count(*) AS n
FROM 's3://shop-data/events/*.parquet'
WHERE ts >= now() - INTERVAL 30 DAY
GROUP BY ALL
ORDER BY n DESC;

Data Types

Type or castExampleReturns
/
instead of a cast error
, 64-bit and 128-bit integers
Exact money values
/ Local time versus a point in time
TIMESTAMPTZ from epoch seconds
Seconds since 1970
List, indexes start at 1
Struct
List of structs
Map
Fixed set of strings, stored compactly

Example: find the rows in whose amount or timestamp would fail a typed load.

sql
-- find the rows that would break a typed load
SELECT order_id, amount, created_at
FROM read_csv('orders.csv', all_varchar = true)
WHERE TRY_CAST(amount AS DECIMAL(10,2)) IS NULL
   OR TRY_CAST(created_at AS TIMESTAMP) IS NULL;

Python API

(install failing?). Deeper coverage goes on a separate Python page.

CommandWhat it does
Query on the default in-memory connection
Open a database file (no attribute connect?)
Open read-only, several processes can share it
Lazy relation, runs when you fetch or show it
Run now with bound parameters
, , Fetch as pandas, Polars or Arrow
, Fetch as Python tuples
Query a pandas or Polars DataFrame by its variable name
Expose a DataFrame under a fixed name
Write a relation to Parquet; too
Release the file lock, or use

Example: fetch the top 10 paid customers as a pandas DataFrame and save them to Parquet.

python
import duckdb

with duckdb.connect("shop.duckdb") as con:
    top = con.execute(
        "SELECT customer_id, sum(amount) AS total FROM orders "
        "WHERE status = ? GROUP BY ALL ORDER BY total DESC LIMIT 10",
        ["paid"],
    ).df()

    con.sql("FROM top").write_parquet("top_customers.parquet")

Gotchas

  • Only one process can open a file for writing. A second writer, or a reader while a writer holds it, fails with a lock error. Open readers with or , and close the Python connection when done.
  • with no file name is in-memory. Everything you create is lost on unless you passed a path or ran .
  • overwrites an existing file without asking, and drops the old table first. Partitioned writes refuse a non-empty folder unless you add , which deletes what was there.
  • The CSV sniffer reads the first 20,480 rows. A bad value further down, like in a number column, fails the whole read with a cast error. Use , set , or load with and .
  • The arrow lambda is deprecated in 1.5 and prints a warning. Write .
  • fails when a table has both a primary key and a separate column. Use and name the key.

Work with DuckDB without memorizing it

1bench is a desktop GUI for DuckDB. Connect, browse your data and run queries in one window, with SQL still there when you want it.

Open DuckDB in 1bench

DuckDB FAQ

How do I create a DuckDB database?
Open a file path that does not exist yet: `duckdb shop.duckdb` in the shell, `duckdb.connect("shop.duckdb")` in Python, or `ATTACH 'shop.duckdb'` from an open session. There is no CREATE DATABASE statement. Run `duckdb` with no path and you get an in-memory database that is gone when you exit, so pass a file name whenever you want tables to persist.
Which SQL dialect does DuckDB use?
DuckDB's parser started from PostgreSQL's, so most Postgres SQL runs unchanged: the same casts with `::`, `ILIKE`, `ON CONFLICT`, window functions and CTEs. On top of that it adds its own shortcuts such as FROM-first queries, `GROUP BY ALL`, `SELECT * EXCLUDE`, `QUALIFY`, `PIVOT` and list comprehensions. Postgres extensions and PL/pgSQL do not exist; use macros instead of stored procedures.
Can DuckDB read Excel files?
Yes, `.xlsx` only, through the core excel extension: `FROM read_xlsx('orders.xlsx', sheet = 'Q3')`, with `range = 'A1:E200'` to read part of a sheet. Older binary `.xls` files are not supported, so save them as `.xlsx` or CSV first. Numbers come back as DOUBLE, so cast ID columns to BIGINT after reading.
Does DuckDB have indexes?
Every column gets automatic min-max zone maps, which is what speeds up filtered scans. `PRIMARY KEY`, `UNIQUE` and `CREATE INDEX` build ART indexes, which help point lookups that return a few rows and enforce constraints, but do little for analytical queries and slow down bulk inserts. For large loads, create the table without them or add them after the data is in.
Where are DuckDB extensions stored?
In `~/.duckdb/extensions/<version>/<platform>/`, for example `~/.duckdb/extensions/v1.5.6/osx_arm64/excel.duckdb_extension`. Each DuckDB version keeps its own folder, so upgrading means extensions download again. `SET extension_directory = '/path'` moves them, which helps on machines with a read-only home directory, and `FROM duckdb_extensions()` shows the install path of each one.