SQL on Parquet
How to run SQL on a Parquet file in your browser
Open a .parquet or .pq file in rows.page to run SQL for free with no signup: up to 1,000,000 result rows and no 1.5 GB size check. Your file stays inside your browser unless you press Share.
No file handy? Open NYC taxi trips, April 2019 (7.4M rows, 127 MB).
Query a Parquet file with SQL in rows.page
Drop your .parquet or .pq file onto rows.page, or choose it from disk, and it opens in your browser through DuckDB-WASM. CSV, TSV, JSON and JSONL files hit a 1.5 GB limit per browser tab, but Parquet skips that check because DuckDB reads it in place. Nothing is uploaded unless you press Share, and the grid has no row limit: rows load 100 at a time as you scroll.
To run SQL, type one statement starting with SELECT, WITH or FROM into the filter box at the top of the table, or press the SQL button to turn your current filter into a query. The table is always called data. It's DuckDB SQL, so window functions, QUALIFY, unnest(), PIVOT inside a subquery and FROM (SUMMARIZE data) all work.
- Open rows.page and drop in your
.parquetor.pqfile. - Type a query against
datainto the filter box, or press SQL to start from your current filter. - Press Enter. The grid shows up to 1,000,000 result rows and marks a longer result "first 1M".
- Open the Export menu to save the result as CSV, JSON, JSONL, Parquet (zstd) or a Markdown table of the first 1,000 rows.
| Task | SQL |
|---|---|
| Count by group | SELECT category, count(*) AS n FROM data GROUP BY category ORDER BY n DESC |
| Top N rows | SELECT * FROM data ORDER BY amount DESC LIMIT 10 |
| Date filter | SELECT * FROM data WHERE created_at >= '2026-01-01' |
| Top row per group with QUALIFY | SELECT * FROM data QUALIFY row_number() OVER (PARTITION BY category ORDER BY amount DESC) = 1 |
| Group by a nested field | SELECT "user.city", count(*) AS n FROM data GROUP BY "user.city" |
| De-duplicate rows | SELECT DISTINCT * EXCLUDE (__rid) FROM data |
| Keep original file order | SELECT * FROM data ORDER BY __rid |
| Column summary statistics | FROM (SUMMARIZE SELECT * EXCLUDE (__rid) FROM data) |
Watch for two quirks. The table has a hidden __rid row-number column, so SELECT DISTINCT * and SUMMARIZE pick it up unless you write * EXCLUDE (__rid), and DISTINCT doesn't keep the file order. Nested Parquet fields become columns with dots in their names, like "user.city", which need double quotes. rows.page also works on one file at a time, so you can't join two local Parquet files, edit cells or run more than one statement at once.
Why Parquet is fast for SQL
Parquet is a columnar format, so DuckDB doesn't have to parse the whole file the way it does with text formats like CSV or JSONL. When a query touches three columns out of fifty, column pruning lets the engine read just the byte ranges for those three columns and skip the rest.
Parquet files are also split into row groups that store per-column statistics such as min and max values. When your query has a WHERE filter, DuckDB checks those statistics first and skips row groups that can't match. In DuckDB's December 2024 benchmark on TPC-H scale factor 20, query 1 ran in 0.88 seconds on a Snappy-compressed Parquet file and 6.72 seconds on the same data as CSV, and the file was 3.78 GB instead of 15.95 GB.
Run SQL on Parquet files with the DuckDB CLI
When you need to join files or export more than 1,000,000 rows, use the DuckDB CLI on your machine. To query a local Parquet file, put its path in single quotes in the FROM clause.
SELECT * FROM 'file.parquet';If your dataset is split across a folder of files with the same schema, pass a glob pattern to read_parquet() to query them all as one table.
SELECT * FROM read_parquet('data/*.parquet');Files that share a key join like tables, and COPY writes a result to a file with no row limit.
SELECT o.order_id, o.amount, c.name
FROM 'orders.parquet' o
JOIN 'customers.parquet' c USING (customer_id);
COPY (SELECT * FROM 'file.parquet' WHERE amount > 10) TO 'result.csv' (HEADER);You can also inspect a file without scanning its rows. parquet_schema() lists the column names and types, and parquet_metadata() shows each row group's statistics and compression.
SELECT * FROM parquet_schema('file.parquet');
SELECT * FROM parquet_metadata('file.parquet');Query Parquet with SQL in Python
In Python, the duckdb package runs the same SQL on local Parquet files without loading the whole file into a DataFrame first. .show() prints a preview, and .df() returns the result as a pandas DataFrame.
import duckdb
duckdb.sql("SELECT * FROM 'file.parquet' LIMIT 10").show()
totals = duckdb.sql("""
SELECT category, sum(amount) AS total
FROM read_parquet('data/*.parquet')
GROUP BY category
ORDER BY total DESC
""").df()
print(totals)Export or share your Parquet query results
The Export menu downloads the current view, whether that's a SQL result or a filtered grid. CSV is comma-delimited UTF-8 without a BOM, and cells starting with =, +, -, @, a tab or a carriage return get a leading apostrophe so spreadsheets don't run them as formulas. You can also export Parquet with zstd compression, JSON, JSONL or Markdown (the first 1,000 rows). Filtered views export every matching row, while SQL-result exports stop at 1,000,000 rows.
To send the file to someone, press Share ("Upload and get a link") or run curl -T file.parquet https://rows.page to get a https://rows.page/<10-character id> link. One push, whether from Share, curl or MCP, takes up to 100 MB on every plan. Share links and pushes made without an account expire after 7 days unless you sign in with Google and press Keep it, and pushes without an account are limited to 50 and 1 GB per IP address per UTC day. A Free account ($0) keeps 10 hosted datasets, Pro ($12 per month) 100 and Max ($39 per month) 1,000, and Pro and Max accept files up to 500 MB and 2 GB through multipart uploads on the API.
If the Parquet file is already public, you don't need to upload it: put its URL after rows.page/, like https://rows.page/https://example.com/file.parquet, and share that link. Your browser fetches the file directly, or through the rows.page proxy (up to 100 MB, 30 seconds and 5 redirects) when the host blocks cross-origin requests, and nothing is stored.
rows.page limits and prices
| What | Limit or price |
|---|---|
| Open a file in your browser | Files up to 1.5 GB, $0, no account, nothing uploaded |
| Share a link without an account | $0, files up to 100 MB, link deleted 7 days after creation |
| Free account | $0, 10 datasets kept, last 3 versions |
| Pro | $12/month, 100 datasets, files up to 500 MB, private links |
| Max | $39/month, 1,000 datasets, files up to 2 GB, private links |
Questions
Does rows.page upload my Parquet file when I run SQL on it?
No. Opening and querying a local file runs in your browser with DuckDB-WASM, and the file is only uploaded if you press Share.
What is the file size limit for Parquet in rows.page?
Local Parquet files skip the 1.5 GB per-tab check that applies to CSV, TSV, JSON and JSONL, because DuckDB reads Parquet in place. To share one, a single push from the Share button, curl or MCP takes up to 100 MB on every plan; Pro ($12 per month) and Max ($39 per month) accept files up to 500 MB and 2 GB through multipart uploads on the API (POST /api/v1/uploads).
Why does SELECT DISTINCT * return every row in rows.page?
The data table has a hidden row-number column, __rid, that makes every row unique. Exclude it to de-duplicate: SELECT DISTINCT * EXCLUDE (__rid) FROM data.
How do I get mean, median or standard deviation for Parquet columns in rows.page?
The Columns panel shows each column's type, distinct count, percentage of empty values, min and max for numbers and dates, and a histogram or the top 10 values, but not mean, median or standard deviation. For those, run SELECT avg(amount), median(amount), stddev(amount) FROM data in the filter box, or FROM (SUMMARIZE SELECT * EXCLUDE (__rid) FROM data) for every column at once.
Can I join two Parquet files with SQL in rows.page?
No. rows.page opens one file at a time as the table data, so there's no second table to join. Use the DuckDB CLI or Python instead: SELECT * FROM 'orders.parquet' JOIN 'customers.parquet' USING (customer_id) joins two files, and read_parquet('data/*.parquet') stacks a folder of files with the same schema.