| name | cli-duckdb |
| description | Covers effective use of DuckDB for in-process analytical SQL queries directly on CSV, Parquet, JSON, and remote S3/HTTP files without importing data. Activates when the user asks about DuckDB, querying files with SQL, analytical data processing, or OLAP on local files.
|
DuckDB โ Analytical SQL Engine
Repo: https://github.com/duckdb/duckdb
In-process analytical database that queries CSV, Parquet, JSON, and remote
files directly with SQL โ no server, no import step. Designed for OLAP
workloads: aggregations, window functions, and large scans run significantly
faster than SQLite or Pandas on the same data.
When to Activate
Manual triggers:
- "How do I use DuckDB?"
- "Query a CSV/Parquet file with SQL"
- "Analytical SQL on local files"
- "Faster than pandas for data analysis"
Auto-detect triggers:
- User wants to run SQL directly on CSV or Parquet files without a database
- User wants window functions, PIVOT, or QUALIFY for analytical queries
- User wants to query S3 or HTTP files without downloading them
- User wants to convert between CSV, Parquet, and JSON formats
- User wants approximate aggregations or SUMMARIZE on large datasets
Key CLI Commands
Opening DuckDB
duckdb
duckdb mydb.duckdb
duckdb -c "SELECT 42"
duckdb -json -c "SELECT 1"
duckdb -csv -c "SELECT 1"
CLI Dot-Commands
.mode line/column/csv/json/markdown/box
.timer on/off
.tables
.schema tablename
.read script.sql
.output results.csv
.quit / .exit
.help
Querying Files Directly
SELECT * FROM 'data.csv' LIMIT 10;
SELECT * FROM read_csv_auto('data.csv');
SELECT * FROM 'data.parquet' LIMIT 10;
SELECT * FROM read_parquet('data.parquet');
SELECT * FROM read_json_auto('events.json');
SELECT * FROM read_json_auto('logs/*.json');
SELECT * FROM read_csv_auto('data_*.csv');
SELECT * FROM read_parquet(['jan.parquet', 'feb.parquet', 'mar.parquet']);
COPY TO โ Export Results
COPY (SELECT * FROM 'data.parquet' WHERE year = 2024)
TO 'filtered.csv' (HEADER, DELIMITER ',');
COPY (SELECT * FROM read_csv_auto('raw.csv'))
TO 'output.parquet' (FORMAT PARQUET, COMPRESSION ZSTD);
COPY (SELECT id, name FROM users)
TO 'users.json' (FORMAT JSON, ARRAY true);
Analytical SQL Features
SUMMARIZE โ Instant Data Profile
SUMMARIZE 'data.csv';
GROUP BY ALL
SELECT region, product, SUM(revenue), AVG(discount)
FROM 'sales.csv'
GROUP BY ALL
ORDER BY SUM(revenue) DESC;
Window Functions
SELECT
date,
revenue,
SUM(revenue) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS rolling_7d,
LAG(revenue, 7) OVER (ORDER BY date) AS last_week,
NTILE(4) OVER (ORDER BY revenue DESC) AS quartile
FROM 'daily_revenue.csv';
CTEs
WITH base AS (
SELECT user_id, COUNT(*) AS sessions, SUM(duration) AS total_time
FROM read_json_auto('events.json')
WHERE event = 'session_start'
GROUP BY ALL
),
ranked AS (
SELECT *, PERCENT_RANK() OVER (ORDER BY total_time) AS pct
FROM base
)
SELECT * FROM ranked WHERE pct >= 0.95;
PIVOT / UNPIVOT
PIVOT (SELECT month, region, revenue FROM 'sales.csv')
ON region
USING SUM(revenue)
GROUP BY month;
UNPIVOT wide_table
ON (jan, feb, mar, apr)
INTO NAME month VALUE revenue;
QUALIFY (filter window function results)
SELECT region, product, revenue,
RANK() OVER (PARTITION BY region ORDER BY revenue DESC) AS rnk
FROM 'sales.csv'
QUALIFY rnk = 1;
List, Struct, and Map Types
SELECT user_id, LIST(event ORDER BY ts) AS event_sequence
FROM read_json_auto('events.json')
GROUP BY user_id;
SELECT payload.user.email FROM read_json_auto('logs.json');
SELECT user_id, UNNEST(tags) AS tag FROM read_json_auto('posts.json');
Regex
SELECT * FROM 'logs.csv'
WHERE regexp_matches(message, 'ERROR|FATAL');
SELECT regexp_extract(url, 'https?://([^/]+)', 1) AS domain
FROM 'access_log.csv';
Approximate Aggregations
SELECT approx_count_distinct(user_id) FROM 'events.parquet';
SELECT * FROM 'data.parquet' USING SAMPLE 1%;
SELECT * FROM 'data.parquet' USING SAMPLE 10000 ROWS;
Advanced Patterns
httpfs โ Query S3 and HTTP Files Directly
INSTALL httpfs;
LOAD httpfs;
SELECT * FROM read_parquet('https://example.com/data.parquet') LIMIT 5;
SET s3_region='us-east-1';
SET s3_access_key_id='...';
SET s3_secret_access_key='...';
SELECT * FROM read_parquet('s3://my-bucket/data/*.parquet');
SELECT * FROM read_parquet('s3://bucket/events/year=*/month=*/*.parquet',
hive_partitioning=true)
WHERE year = 2024 AND month = 3;
Cross-Format Joins
SELECT c.name, p.revenue
FROM 'customers.csv' c
JOIN 'transactions.parquet' p ON c.id = p.customer_id
WHERE p.amount > 1000;
CTAS โ Create Table As Select
CREATE TABLE top_customers AS
SELECT customer_id, SUM(amount) AS ltv
FROM 'orders.parquet'
GROUP BY ALL
ORDER BY ltv DESC
LIMIT 1000;
Parquet Export Optimization
COPY (SELECT * FROM 'large.csv')
TO 'partitioned/' (FORMAT PARQUET, PARTITION_BY (year, region), COMPRESSION ZSTD);
Attach SQLite
INSTALL sqlite;
LOAD sqlite;
ATTACH 'app.db' AS sqlite (TYPE sqlite);
SELECT * FROM sqlite.users LIMIT 10;
SELECT d.event, s.name
FROM read_json_auto('events.json') d
JOIN sqlite.users s ON d.user_id = s.id;
Practical Examples
duckdb -c "SUMMARIZE 'data.csv'"
duckdb -c "SELECT COUNT(*) FROM 'large.parquet'"
duckdb -c "COPY (SELECT * FROM 'data.csv') TO 'data.parquet' (FORMAT PARQUET)"
duckdb -c ".read analysis.sql"
duckdb -json -c "SELECT * FROM 'data.csv' LIMIT 5"
duckdb -c "SELECT * FROM (SUMMARIZE 'data.csv') WHERE column_name = 'revenue'"
Chaining with Other Skills
- jq: Pipe DuckDB JSON output to jq for further reshaping; or preprocess nested JSON with jq before querying with
read_json_auto()
- SQLite (cli-sqlite): Use DuckDB for heavy analytical work then export results to SQLite (
ATTACH ... TYPE sqlite) for lightweight app consumption; share data both ways
- fd (cli-fd): Use fd to build file lists for multi-file queries:
fd -e parquet | xargs -I{} duckdb -c "SELECT COUNT(*) FROM '{}'" or pass a glob to read_parquet()
- bat (cli-bat): Use
bat -l sql to view and syntax-highlight SQL scripts before passing them to duckdb -c ".read script.sql"
- Apify / web scraping: Export scraped data to CSV or JSON, then use DuckDB to analyze without any import step