How to Export SQL Query Results to Excel
Last updated August 6, 2026 · By the SaturnSQL team
Every SQL client can get rows into Excel somehow. The difference between the methods is what they quietly damage on the way: stripped leading zeros, IDs turned into scientific notation, product codes reinterpreted as dates, and result sets truncated at whatever the grid happened to display.
This guide covers the six methods people actually use, what each one costs you, and which to pick for your situation. The last section covers the question people ask second: how to stop doing this by hand every Monday.
The short version
A CSV is not an Excel file. Most “export to Excel” features write CSV and let Excel guess the types, which is the root cause of nearly every corrupted export. If the data has leading zeros, long IDs, or dates in it, use a method that writes a genuine .xlsx workbook: SaturnSQL, Oracle SQL Developer, or DBeaver. Everything else is a tradeoff.
Method 1: a one-click .xlsx download
SaturnSQL is a browser-based SQL editor. Run a query, open the export menu above the results grid, and pick XLSX. The file is written with a real spreadsheet library, so numbers stay numbers, dates stay dates, and Excel opens it without an import dialog or a type-conversion warning.
The row behaviour is worth knowing, because this is where most tools quietly stop. On PostgreSQL and MySQL the export streams row by row as the database produces them, so there is no row cap at all and the only bound is the query timeout. On SQL Server, BigQuery, ClickHouse, and DynamoDB the export is buffered instead and capped at 100,000 rows. Either way the export is not limited to what the grid displayed, which is a common trap in clients where “export” means “save what is on screen”.
Supported: PostgreSQL, MySQL/MariaDB, SQL Server, Redshift, ClickHouse, BigQuery, and DynamoDB. Not supported: Oracle and Snowflake. CSV and JSON are available from the same menu if you want them.
Method 2: SQL Server and the SSMS export wizard
This is the most-searched version of the question and it has an annoying answer: SSMS cannot export the results grid to Excel directly. Right-click the grid, choose “Save Results As”, and the only formats offered are CSV and text. There is no .xlsx option.
To get a real Excel file out of SQL Server you need the Import and Export Wizard:
- In Object Explorer, right-click the database, then Tasks → Export Data.
- Source: SQL Server Native Client or OLE DB, pointed at your database.
- Destination: Microsoft Excel, then pick a file path and Excel version.
- Choose “Write a query to specify the data to transfer” and paste your SELECT.
- Review the column mappings, then run.
Two things bite people here. First, the Excel destination is provided by the Microsoft Access Database Engine (ACE OLEDB), which is a separate download and has to match your SSMS bitness, so a 64-bit SSMS with a 32-bit engine installed will simply not list Excel as a destination. Second, the wizard is strict about types and will fail the entire transfer over one column it cannot map, usually a wide nvarchar or a datetime2.
If you only need this once and the data is simple, “Save Results As” to CSV and opening that in Excel is genuinely faster. Just read the copy-paste warnings below first, because the same type-guessing applies to opening a CSV.
Method 3: sqlcmd, bcp, psql, and mysql for repeatable exports
Command-line exports produce CSV rather than .xlsx, but they are scriptable, they run on a server without a GUI, and they do not care how large the result set is. This is the right choice when the export needs to live in a shell script or a container.
SQL Server, with sqlcmd:
sqlcmd -S localhost -d SalesDB -E \ -Q "SELECT order_id, customer, total FROM orders" \ -o orders.csv -s"," -W -h-1
-s sets the separator, -W trims trailing spaces (without it every column is padded to its full width), and -h-1 removes the header underline that would otherwise become a row of dashes in your spreadsheet. For very large extracts, bcp is faster but has no header row at all.
PostgreSQL, with psql:
\copy (SELECT order_id, customer, total FROM orders) \ TO 'orders.csv' WITH CSV HEADER
Use \copy (backslash, a psql command) rather than COPY. COPY writes on the database server and needs superuser or the pg_write_server_files role; \copy writes on your own machine with no extra privileges. This distinction accounts for most “permission denied for COPY” questions.
MySQL:
mysql -u reporting -p -e \ "SELECT order_id, customer, total FROM orders" \ --batch --raw sales > orders.tsv
This gives tab-separated output, which Excel handles well. The SELECT ... INTO OUTFILE form produces cleaner CSV but writes on the database server and requires the FILE privilege with a path inside secure_file_priv, so it is usually unavailable on managed hosts like RDS.
Method 4: Oracle SQL Developer
SQL Developer has the best built-in Excel export of any free vendor client, and it writes genuine .xlsx rather than CSV.
Run your query, right-click anywhere in the results grid, choose Export, set Format to xlsx, pick a destination file, and finish. You can export the whole result set rather than just the fetched rows, which matters because the grid only fetches the first 50 by default until you scroll.
For very large extracts the wizard gets slow, and the faster route is a hint comment in the query itself. Put /*csv*/ immediately after SELECT and run the statement as a script with F5; SQL Developer streams delimited output straight to the Script Output pane, which you can redirect to a file with SPOOL. Note that SaturnSQL does not connect to Oracle, so if Oracle is your database, SQL Developer’s own export is the answer here.
Method 5: DBeaver and other cross-engine GUIs
If you work across several database engines from one desktop client, DBeaver handles Excel export uniformly. Right-click the results grid, choose Export resultset, and pick XLSX as the target. On some builds the XLSX exporter ships as the separate “Office Formats” extension from the DBeaver marketplace, so if you only see CSV and SQL in the format list, that is what is missing.
The export wizard also lets you set the null string, timestamp format, and whether headers are included, which is useful when the file feeds a downstream template. It is a heavier tool than the alternatives, so see our DBeaver alternatives comparison if that is a concern.
Method 6: copy-paste, and exactly when it lies to you
Selecting the grid, copying with headers, and pasting into Excel is what most people actually do, and for a quick eyeball of a few hundred rows of text and integers it is completely fine. It is worth knowing precisely when it is not, because the failures are silent. Excel type-guesses every pasted cell:
- Leading zeros vanish. Product code
007becomes7. Zip code02134becomes2134. This is the single most common corrupted export. - Anything date-shaped becomes a date. A version string
3-4becomes 3 April.1/2becomes 2 January. Gene names are the famous case, but SKUs and internal reference codes hit it constantly. - Long integers lose precision. Excel stores numbers as 64-bit floats with 15 significant digits. A 19-digit Snowflake-style ID or a bigint primary key is rounded and displayed as
1.23457E+18. The original value is gone, not hidden. - NULL and empty string become identical. Both arrive as an empty cell, so you can no longer tell “no value recorded” from “recorded as blank”.
- You get only what was fetched. Most clients render a capped preview, often 1,000 rows. Copying the grid copies the preview, and nothing warns you that the other 40,000 rows were never there.
If you must paste, the defence is to paste into columns pre-formatted as Text, or to use Data → From Text/CSV and set the column types explicitly in the import dialog rather than accepting the preview. Exporting a real .xlsx sidesteps all of it, because the cell types are written into the file instead of guessed at paste time.
Exporting to Excel automatically, on a schedule
Most people who export a query to Excel once end up exporting it every week. The manual loop is familiar: open the client, run the saved query, export, open the workbook, delete last week’s rows, paste, fix the date column, re-point the pivot table, send it round.
Automating it means handing both halves to one tool: the database connection and the destination. In SaturnSQL you save the query, then attach a schedule with a cron cadence and an IANA timezone, and each run writes the fresh rows to the destination. You can preview the next five run times before saving, so a cadence never surprises you.
Worth being precise about the destination
Scheduled runs deliver to a Google Sheet or to Slack. SaturnSQL does not currently email you an .xlsx attachment on a timer, and it would be misleading to imply otherwise. If a file in your inbox is a hard requirement, a scheduled script around bcp or \copy plus a mail step is the honest answer.
For the recurring-report case, a Sheet is usually the better destination anyway. It is one click from Excel (File → Download → Microsoft Excel (.xlsx)), and unlike a mailed attachment the URL stays stable, so pivot tables, IMPORTRANGE formulas, and anything else pointing at it keep working run after run. Nobody has to hunt for the newest version in a mail thread.
The mechanics are covered in more detail in the SQL to Google Sheets guide, and the scheduling side in scheduled SQL queries and automated SQL reports.
Which method should you use?
| Situation | Use | Real .xlsx? |
|---|---|---|
| One-off, data has IDs or leading zeros | SaturnSQL, SQL Developer, or DBeaver export | Yes |
| Very large result set | Streamed export (Postgres/MySQL), or bcp | Yes / No |
| Needs to run in a script or container | sqlcmd, \copy, mysql --batch | No, CSV |
| Stuck in SSMS, Excel destination required | Import/Export Wizard + ACE OLEDB | Yes |
| Oracle database | SQL Developer export wizard | Yes |
| Same report every week | Scheduled export to a Google Sheet | Via one click |
| Quick look, plain text and integers only | Copy with headers, paste | No |
Frequently asked questions
How do I export SQL query results to Excel automatically?
Put the query on a schedule with a tool that owns both the database connection and the destination. SaturnSQL runs a saved query on a cron cadence in a timezone you choose and writes the rows into a Google Sheet or posts them to Slack. A Sheet is one click from Excel and the link stays stable, so downstream formulas keep working. Scheduled delivery is to Sheets or Slack, not an emailed .xlsx attachment.
Why does Excel change my SQL data when I paste it?
Excel guesses a type for every pasted cell. Leading zeros get stripped, date-shaped values get converted, and integers longer than 15 digits are rounded into scientific notation. Writing a real .xlsx file avoids it because the types are recorded in the file rather than inferred on paste.
Can SSMS export query results directly to Excel?
Not from the results grid: “Save Results As” offers CSV and text only. Reaching Excel needs the Import and Export Wizard with the Microsoft Access Database Engine installed, matching your SSMS bitness.
How many rows can I export to Excel?
Excel stops at 1,048,576 rows per sheet. Tools usually stop sooner. SaturnSQL streams on PostgreSQL and MySQL with no row cap, bounded only by the query timeout, and caps buffered exports at 100,000 rows on SQL Server, BigQuery, ClickHouse, and DynamoDB.
Is a CSV the same as an Excel file?
No. A CSV is plain text with no type information, so Excel infers a type for every cell when it opens the file, which is where corruption creeps in. An .xlsx is a real workbook that records each cell’s type explicitly.
Which databases can SaturnSQL export to Excel?
PostgreSQL, MySQL/MariaDB, Microsoft SQL Server, Amazon Redshift, ClickHouse, BigQuery, and Amazon DynamoDB. Oracle and Snowflake are not supported; for Oracle, SQL Developer’s own export wizard writes .xlsx directly.
Export a real .xlsx in one click, or put the query on a schedule and stop exporting it at all.