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.

.parquet
Drop a file here
Stays in your browserUp to 1.5 GB

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.

  1. Open rows.page and drop in your .parquet or .pq file.
  2. Type a query against data into the filter box, or press SQL to start from your current filter.
  3. Press Enter. The grid shows up to 1,000,000 result rows and marks a longer result "first 1M".
  4. Open the Export menu to save the result as CSV, JSON, JSONL, Parquet (zstd) or a Markdown table of the first 1,000 rows.
Common DuckDB SQL queries on data in rows.page
TaskSQL
Count by groupSELECT category, count(*) AS n FROM data GROUP BY category ORDER BY n DESC
Top N rowsSELECT * FROM data ORDER BY amount DESC LIMIT 10
Date filterSELECT * FROM data WHERE created_at >= '2026-01-01'
Top row per group with QUALIFYSELECT * FROM data QUALIFY row_number() OVER (PARTITION BY category ORDER BY amount DESC) = 1
Group by a nested fieldSELECT "user.city", count(*) AS n FROM data GROUP BY "user.city"
De-duplicate rowsSELECT DISTINCT * EXCLUDE (__rid) FROM data
Keep original file orderSELECT * FROM data ORDER BY __rid
Column summary statisticsFROM (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.

sql
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.

sql
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.

sql
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.

sql
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.

python
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

WhatLimit or price
Open a file in your browserFiles 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.