Quick answer
- In 1bench
- Add a DuckDB connection, choose Remote URL, paste an s3:// or https:// link to a Parquet, CSV or JSON file, and it opens as a table in the sidebar.
- In SQL
- sql
SELECT channel, count(*) FROM 'sales.csv' GROUP BY channel;A quoted file path works anywhere a table name does. Switch to read_csv, read_parquet or read_json when you need options.
Query files visually in 1bench
1bench opens a single Parquet, CSV or JSON file on S3, MinIO, R2 or HTTPS as a DuckDB connection, with the file shown as a table you can browse. Local files and anything more involved go through the SQL editor in the Query panel.
Prerequisite: download 1bench. Free 7-day trial, no sign-up.
- 1
Add a DuckDB connection
Click the + button (Add connection) at the top of the sidebar, or New connection on the start screen, and pick DuckDB.
- 2
Choose Remote URL
Under Connection mode, click Remote URL and paste a link such as s3://shop-data/sales.parquet or https://host/data.csv. Table name (optional) sets the name shown in the sidebar; by default it comes from the file name.
- 3
Add S3 credentials if the bucket is private
Expand S3 credentials (optional) and fill in Access key ID, Secret access key and Region. For MinIO, R2 or another S3-compatible store, also fill in Endpoint (MinIO / R2 / custom), for example localhost:9000.
- 4
Test and save
Test Connection reads zero rows from the file to check the link and the keys. Save Connection connects, and the file appears in the sidebar as a view you can open and browse like any table.
- 5
Write SQL for everything else
For local files, globs, Excel or joins, right-click any DuckDB connection (In-memory works well for scratch work), choose New Query and type the SQL from the sections below. Click Run or press Cmd+Enter (Ctrl+Enter on Windows and Linux).
- Nothing is copied. A Remote URL connection is an in-memory DuckDB with a view over read_parquet, read_csv_auto or read_json_auto, so each read goes back to the file.
- The format comes from the extension: .parquet and .pq, .csv, .csv.gz and .tsv, or .json, .ndjson, .jsonl and .json.gz. Any other link is read as Parquet, and a failure then ends with "Supported files: Parquet, CSV, JSON and NDJSON." Excel files need read_xlsx in the Query panel.
- An Endpoint without a scheme and with a port other than 443 is reached over plain HTTP, and 1bench always uses path-style bucket URLs when an Endpoint is set. Write https:// in front to force TLS.
- The keys are set on the connection itself, so New Query on a Remote URL connection can read other files in the same bucket without a CREATE SECRET.
- The Query panel adds a LIMIT of one page (100 rows by default) to a SELECT that has none. DuckDB's FROM-first shorthand does not get one, so add your own LIMIT when browsing a large file that way.
Query files with SQL
DuckDB treats a file path as a table. Put the path in quotes after , and DuckDB works out the format from the extension and the column names and types from the data. Nothing is loaded into the database, so the file can sit on your disk, on S3 or behind an HTTPS link.
SELECT channel, count(*) AS orders, sum(gross_cents) / 100 AS revenue
FROM 'sales.csv'
GROUP BY channel
ORDER BY revenue DESC;
The examples use the 1bench demo shop: , and , exported to files. Relative paths resolve from the folder DuckDB was started in. Every statement below gives the same results on DuckDB 1.4, 1.5 and 2.0.
Query a CSV file
sniffs the delimiter, quoting, header and column types from the first 20,480 rows. Call it directly when you need to override any of that:
| Option | What it does | Example |
|---|---|---|
| First row holds column names (auto-detected) | ||
| Column separator (auto-detected) | ||
| Column names for a file without a header | ||
| Force a type for some columns | ||
| Read every column as text | ||
| Parse dates in a given format | ||
| Rows used to guess types; -1 reads them all | ||
| Skip rows that fail to parse | ||
| Add a column with the source file path |
Example: read a semicolon-separated export with no header row.
SELECT *
FROM read_csv('customers_noheader.txt', delim = ';', header = false,
names = ['id', 'name', 'email', 'country', 'plan', 'signup_date', 'ltv'])
LIMIT 5;
Type detection only looks at the sample. If the first 30,000 rows of are numbers and row 30,001 says , the query fails partway through with "Conversion Error: CSV Error on Line: 30002 ... Could not convert string "n/a" to 'BIGINT'". Fix it with , (the column then reads as VARCHAR) or (the bad row is dropped). Compressed files such as are read directly, and reads a CSV piped into the shell.
Query a Parquet file
Parquet stores column types and per-column statistics, so there is nothing to sniff and DuckDB reads only the columns a query uses.
SELECT product_id, sum(quantity) AS units
FROM 'sales.parquet'
WHERE occurred_at >= '2025-10-01'
GROUP BY product_id
ORDER BY units DESC
LIMIT 10;
shows the row groups and the min and max of each column, which is what DuckDB uses to skip data, and lists the stored types.
SELECT path_in_schema, type, stats_min, stats_max
FROM parquet_metadata('sales.parquet');
Query files on S3
The httpfs extension handles , and paths, and DuckDB installs and loads it the first time you use one. Store the keys once as a secret; it lasts for the session, or use to keep it.
CREATE SECRET (TYPE s3, KEY_ID 'AKIA...', SECRET '...', REGION 'us-east-1');
SELECT count(*) FROM 's3://shop-data/sales.parquet';
Without a secret, a private bucket answers "HTTP 403 Forbidden ... No credentials are provided". Wrong keys give the same 403 with "Credentials are provided, but they did not work". picks up your AWS CLI profile or environment variables instead of keys typed into SQL.
For MinIO, Cloudflare R2 or another S3-compatible store, add the endpoint and path-style URLs. Leave out on a plain HTTP server and you get "SSL connect error".
CREATE SECRET minio (
TYPE s3, KEY_ID 'minio', SECRET 'minio12345',
ENDPOINT 'localhost:9000', URL_STYLE 'path', USE_SSL false,
SCOPE 's3://shop-data'
);
ties a secret to one bucket or prefix, so you can keep separate keys for several buckets in one session. lists them. Public files need no secret at all:
SELECT count(*) FROM 'https://shell.duckdb.org/data/tpch/0_01/parquet/lineitem.parquet';
Query JSON files
accepts both newline-delimited JSON (one object per line) and a single top-level array, and turns nested objects into STRUCT columns and nested arrays into lists.
SELECT name, country FROM read_json('customers.json');
Example: reach into nested fields with dots, and turn an array of line items into rows with .
SELECT id, customer.name, customer.country FROM 'orders.json';
SELECT id, unnest(items, recursive := true) FROM 'orders.json';
Query Excel files
comes from the excel extension, which also loads itself on first use. It reads files, one sheet at a time.
SELECT name, country, lifetime_value_cents
FROM read_xlsx('customers.xlsx', sheet = 'customers');
SELECT * FROM read_xlsx('customers.xlsx', range = 'A1:C4');
Excel keeps every number as floating point, so and come back as DOUBLE (). Cast them, for example , before joining on them.
Query multiple files with a glob
A glob or a list of paths reads many files as one table. matches inside one folder and matches any depth. Add to see which file each row came from.
SELECT filename, count(*)
FROM read_csv('exports/*.csv', filename = true)
GROUP BY filename;
When the files do not share the same columns, use . DuckDB then lines columns up by name and fills the gaps with NULL.
SELECT channel, count(*)
FROM read_csv('exports/*.csv', union_by_name = true)
GROUP BY channel;
Without it, DuckDB takes the column list from the first file it reads. If that file has fewer columns, the extra columns in later files are silently dropped: here has no column, so disappears from the whole result and fails with "Referenced column "channel" not found". If the first file has more columns, the query stops with "Schema mismatch between globbed files ... Column with name: "channel" is missing". and take the same and options.
Query hive-partitioned folders
Folders named , such as , are the Hive layout that Spark, Athena and DuckDB's own write. turns the folder names into columns.
SELECT channel, count(*)
FROM read_parquet('lake/*/*.parquet', hive_partitioning = true)
GROUP BY channel;
A filter on a partition column skips whole folders. shows it: with the Read Parquet step lists the filter under File Filters and reports "Scanning Files: 1/4". DuckDB also detects folders when you leave the option out, but setting it keeps the query explicit. The same paths work on S3, for example .
Join files with each other and with tables
Files, S3 objects and tables mix freely in one query, so you can join a Parquet file to a CSV, or a file to a table in an open database.
SELECT c.country, count(*) AS orders, sum(s.gross_cents) / 100 AS revenue
FROM 'sales.parquet' s
JOIN 'customers.csv' c ON c.id = s.customer_id
GROUP BY c.country
ORDER BY revenue DESC
LIMIT 5;
Save a file as a view or a table
A view keeps the path and reads the file on every query, so it always shows the current contents. copies the rows into the database once, which is faster for repeated queries on a CSV.
CREATE VIEW sales_files AS FROM 's3://shop-data/sales.parquet';
CREATE TABLE sales_2025 AS FROM read_csv('sales.csv');
To add a file to a table that already exists, use . lines columns up by position, so name them on both sides when the file's order differs from the table's. does the same in one statement.
From the CLI and Python
The shell opens an in-memory database when you give it no file, which is all you need to query files from your own shell.
duckdb -c "SELECT channel, count(*) FROM 'sales.parquet' GROUP BY ALL"
In Python, runs on a default in-memory connection and returns a relation. Call to print it, for a pandas DataFrame or for tuples.
import duckdb
top = duckdb.sql("""
SELECT channel, count(*) AS orders
FROM 'sales.parquet'
GROUP BY channel
ORDER BY orders DESC
""")
top.show()
df = top.df()
DuckDB 2.0 removed from relations, failing with "Relations no longer have fetchone()", because a relation reruns its query on every call. Use there, or call on a connection's result as before.