Querying files directly

DuckDB · Essentials · 10 min

This is DuckDB's killer feature: answer questions about data files without an import step.

DuckDB treats a file path as a table. Write SELECT * FROM 'sales.csv' and it detects the format from the extension, infers column names and types, and streams the file through the query. The same works for .parquet, .json and compressed files like .csv.gz.

Behind that shortcut are table functions you can call explicitly when you need options: read_csv, read_parquet and read_json. A few patterns you will use daily:

  • DESCRIBE SELECT * FROM 'file.parquet' shows inferred column names and types
  • SUMMARIZE 'file.csv' prints min, max, distinct count, nulls and quantiles for each column
  • CREATE TABLE t AS FROM 'file.csv' imports a file once if you will query it many times

Querying in place is great for exploration, but each query re-reads (and for CSV re-parses) the file. Parquet is columnar and compressed, so DuckDB reads only the columns and row groups a query needs; CSV must be parsed in full. If you run many queries over a big CSV, import it into a table or convert it to Parquet first.

Paths are relative to the process's working directory, and remote URLs work too once the httpfs extension is loaded (it auto-loads for https:// and s3:// in most installations).

Example

import duckdb, os, tempfile

os.chdir(tempfile.mkdtemp())
with open("sales.csv", "w") as f:
    f.write("region,amount\nnorth,10\nsouth,25\nnorth,5\n")

print(duckdb.sql("""
    SELECT region, sum(amount) AS total
    FROM 'sales.csv'
    GROUP BY region ORDER BY total DESC
""").fetchall())   # [('south', 25), ('north', 15)]

print(duckdb.sql("DESCRIBE SELECT * FROM 'sales.csv'").fetchall()[1][:2])
# ('amount', 'BIGINT')

Remember

  • A quoted file path in FROM is a table; the format comes from the extension
  • read_csv, read_parquet, read_json take options when auto-detection is not enough
  • DESCRIBE and SUMMARIZE give a fast first look at an unknown file

Watch out for

  • Re-querying a multi-gigabyte CSV over and over instead of importing it or converting to Parquet once.

Check yourself

  1. Why is querying one column of a Parquet file usually much faster than the same query on a CSV?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: duckdb.org/docs/current/data/csv/overview