Query Excel Files with SQL in Your Browser
An .xlsx workbook often arrives as the final stop before a messy export. The SQL Workbench converts every sheet in that workbook into a separate DuckDB table, named after the sheet tab, so you can query Sheet1 and Summary in the same session and JOIN them without copy-paste formulas.1
Excel as a data source: where it fits and where it struggles
Excel workbooks carry data in a format that every business user understands, which is exactly why they appear in so many pipelines: stakeholders paste updates into a shared sheet, analysts download reports from SaaS platforms as .xlsx, and finance teams maintain ledgers that never migrate to a database. Yet Excel has limits that SQL does not. Formula cells can hide calculation errors, merged cells break tabular structure, and workbooks with hundreds of thousands of rows become slow to navigate. Because the workbench loads the raw cell values into DuckDB tables, you bypass all of those issues. Each sheet becomes a plain table. Formula results appear as their computed values.2 You run SQL against the data, not against the spreadsheet machinery.
How the workbench loads Excel files
Excel files pass through a JavaScript parser that reads the modern XML-based .xlsx format.1 Each sheet in the workbook becomes a DuckDB table named after the sheet tab.3 Column headers come from the first row, and a workbook with sheets named Orders, Products, and Returns produces three tables: Orders, Products, and Returns.
Excel types after parsing
The parser infers column types from the cell values: numeric cells become DOUBLE, date cells become VARCHAR (Excel date serial numbers are converted to ISO strings), and text cells become VARCHAR.3 After parsing, the workbench hands every table to DuckDB, so you query them with the same SQL as any other file format. Parquet export is available for Excel-derived tables because they are loaded into DuckDB rather than sql.js, which means your exported file preserves column types exactly.
Because every column resolves to a single DuckDB type, a column that mixes numbers and text lands as VARCHAR to avoid dropping values, which is why a price column with a stray note becomes text instead of DOUBLE. Cast such columns in the query rather than trusting the preview type, and review the SHOW TABLES output before writing JOINs so a type mismatch does not filter your rows out silently.
Multi-sheet queries and practical patterns
Having multiple sheets as separate tables unlocks queries that spreadsheets handle awkwardly. JOIN the Orders sheet to the Products sheet on a product ID column to compute revenue per product. Use GROUP BY on the Orders sheet alone to find the top customers by spend. Apply window functions like ROW_NUMBER() OVER (PARTITION BY region ORDER BY sale_date DESC) to rank rows within each region across an entire sheet.4 For data quality checks, COUNT(DISTINCT id) versus COUNT(*) identifies duplicate rows in a sheet header that should have unique identifiers. When a workbook contains summary sheets that aggregate from detail sheets, you can verify the summary against the raw data with a single query.
Data quality checks across multiple sheets
Data quality checks across sheets are where the workbench outperforms in-spreadsheet formulas. A single SQL query cross-validates two sheets simultaneously: SELECT o.order_id FROM Orders o LEFT JOIN Products p ON o.product_id = p.id WHERE p.id IS NULL returns all order rows whose product_id has no matching record in Products. A VLOOKUP would handle this only awkwardly, because a spreadsheet formula cannot scan two entire sheets at once and flag every mismatched row in a single pass.
Detecting duplicates and referential integrity failures
For duplicate detection within a sheet, SELECT id, COUNT(*) AS n FROM Orders GROUP BY id HAVING n > 1 returns only IDs that appear more than once. Combine both checks into a single quality session: run the duplicate query on each sheet first, then run the LEFT JOIN referential integrity check across sheets. Export the problem rows to CSV to share findings with the file owner without resending the entire workbook.
Excel date and number formatting in SQL queries
In Excel, date cells are stored internally as numeric serial numbers (days since January 1, 1900) and displayed with a format mask.5 When the workbench loads an Excel file, the JavaScript parser converts date cells to ISO date strings (YYYY-MM-DD) before handing them to DuckDB. The resulting column type is VARCHAR, not DATE.
Working with Excel dates and percentages
To use date arithmetic or date_trunc, cast the column first: sale_date::DATE converts the VARCHAR ISO string to a DuckDB DATE type, which is exactly where Excel dates need a real DATE cast before any date math. Number cells formatted as currencies in Excel (formatted as $1,234.56) arrive as DOUBLE in DuckDB; the currency symbol and comma formatting are presentation concerns the parser strips. Percentage cells arrive as DOUBLE in their decimal form: 25% becomes 0.25. Multiply by 100 when you need the display percentage: SELECT margin_pct * 100 AS margin_pct_display FROM orders.
When to use this
Use this when you receive an .xlsx export from a SaaS platform, a finance system, or a colleague and want to run SQL aggregations, cross-sheet JOINs, or data quality checks without opening the file in Excel. Drop the workbook into the workbench and run SHOW TABLES first to confirm every sheet loaded as its own table before you write a cross-sheet JOIN.
Examples
List all available sheet names as tables
SHOW TABLES;
Returns all sheet names loaded from the workbook. Each name is a queryable table.
Join two sheets on a common column
SELECT o.order_id,
o.quantity,
p.product_name,
p.unit_price
FROM Orders o
JOIN Products p ON o.product_id = p.id; Multiple sheets from the same workbook are available simultaneously as separate tables.
Find the top customers by total spend
SELECT customer_name,
SUM(amount) AS total_spend
FROM Orders
GROUP BY customer_name
ORDER BY total_spend DESC
LIMIT 10; DuckDB sums across all rows in the sheet — no pivot table required.
Check for duplicate IDs in a sheet
SELECT id, COUNT(*) AS n FROM Products GROUP BY id HAVING COUNT(*) > 1;
Returns only rows where the id value appears more than once — a fast data quality check.
- 1.
Microsoft Learn, "Structure of a SpreadsheetML document," learn.microsoft.com, January 2025. https://learn.microsoft.com/en-us/office/open-xml/spreadsheet/structure-of-a-spreadsheetml-document
- 2.
DuckDB Foundation, "Excel Import," duckdb.org, accessed June 2026. https://duckdb.org/docs/current/guides/file_formats/excel_import
- 3.
OfficeDev, "Working with formulas," github.com, January 2025. https://github.com/OfficeDev/open-xml-docs/blob/main/docs/spreadsheet/working-with-formulas.md
- 4.
Microsoft Support, "Change the date system, format, or two-digit year interpretation," support.microsoft.com, accessed June 2026. https://support.microsoft.com/en-US/Excel/change-the-date-system-format-or-two-digit-year-interpretation
- 5.
PostgreSQL Global Development Group, "Window Functions," postgresql.org, accessed June 2026. https://www.postgresql.org/docs/18/functions-window.html