SQL to CSV

Turn the INSERT statements in a SQL dump into a CSV table, without restoring the database.

Runs on your deviceDataWorkingNo account needed

What this tool does

SQL to CSV is a parser, not a database. It scans the text you paste for INSERT … VALUES statements, walks each tuple of values, and writes them out as rows. Quoting is unpicked the way MySQL writes it: doubled quotes, backslash escapes such as \n and \', backtick and bracket identifiers, and type prefixes like N'…', E'…' or DATE '…', where the prefix is dropped and the string body kept. Comments introduced by --, # or /* */ are stripped first, so a whole section of a mysqldump file can go in as it stands.

There is no dialect switch, and backslash escaping is the one place that costs you. It is applied to every paste, so a PostgreSQL dump written with standard_conforming_strings on — where a backslash inside a string means a backslash — loses it: 'C:\path' comes out as C:path, and a \t in the middle of a value becomes a real tab character. Check any column that stores Windows paths, regular expressions or LaTeX before trusting the export.

Column names come from three places, in order. If the statement lists its columns, those names are used. If it does not, a CREATE TABLE for the same table in the same paste supplies them. Failing both, the columns are named column_1, column_2 and so on. Later statements that name the same columns in a different order, or name only some of them, are mapped onto that established order rather than written in the order they arrived — so a partial INSERT fills its own columns and leaves the rest of the row empty instead of shifting every value one place left. The order has to be established first, though: if the opening statement carries no column list and no CREATE TABLE stands above it, its rows are read by position, and a later statement that does name columns sets the header for the whole table — leaving those first rows under the wrong headings.

The rest of the limits are worth knowing before you paste. Only INSERT … VALUES is read: a pg_dump COPY … FROM stdin block, an INSERT … SELECT, and a saved SELECT result all produce an error naming the statement form that was missing. Nothing is evaluated, because there is no SQL engine here — NOW() lands in the cell as the text NOW(), CONCAT('a','b') arrives as its own source text, 0x1f stays written as 0x1f, and X'4869' comes out as 4869 rather than the bytes it encodes. One table is exported per run, the result is copied out of the output pane rather than saved as a .csv file, and the input is a textarea with no file picker, so the work happens in this browser tab and the dump is never sent anywhere, but a multi-gigabyte backup has to be trimmed to the part you want first.

How to use it

  1. Paste the SQL. Drop the dump into the input box, or press Load sample to see the shape it expects. CREATE TABLE lines, comments and statements it does not read are skipped rather than rejected.
  2. Name the table, if the dump holds more than one. Leave the field blank and the first table with INSERT rows is used — a table that appears only as a CREATE TABLE does not count. Schema qualifiers are stripped, so public.users matches users, and the match ignores case. A name with no INSERT rows returns the list of tables actually found.
  3. Decide how the header and NULL are written. The header row of column names is on by default. NULL becomes either an empty cell or the literal word NULL. Writing the word makes a real NULL indistinguishable from a string that already read NULL; the empty cell keeps the two apart.
  4. Pick the output delimiter and copy the result. Comma, semicolon, tab or pipe. Any value holding the delimiter, a quote character or a line break is wrapped in quotes with inner quotes doubled, then the CSV is copied straight out of the output pane with the Copy output button.

Processing & privacy

Runs on your device. Your file is processed directly in this browser.

Common use cases

Getting one table out of a mysqldump backup

A .sql backup holds every table as text. Going from a dump to CSV here reads the rows you want without spinning up a server and restoring the lot.

Opening a phpMyAdmin export in a spreadsheet

A SQL export downloaded from shared hosting is unreadable in Excel. Converting the INSERT block gives you something a spreadsheet actually opens.

Reading a seed or fixture file

Migration seeds are written as statements and reviewed as rows. Converting them shows what is about to land in the table, aligned in columns.

Sending data to someone without database access

Colleagues who cannot connect to the database can still read a CSV, and a tab-delimited copy pastes cleanly into a document or a sheet.

Questions

Is my SQL uploaded anywhere?
No. The parser is JavaScript running in this tab, so the statements, the table names and the row values stay on the machine you typed them on. Dumps often carry email addresses, password hashes and internal identifiers, which is exactly the sort of thing worth not posting to a stranger's server.
It says no INSERT statements were found — why?
The tool reads only INSERT … VALUES. A PostgreSQL dump written with COPY … FROM stdin puts its data in a tab-separated block instead, an INSERT … SELECT copies rows from another table without listing any, and a captured SELECT result is output rather than SQL. None of those carry the value tuples this parser needs.
Where do the column names in the header come from?
The statement's own column list first, then a CREATE TABLE for that table in the same paste, and if neither exists the columns are numbered column_1 onwards. That last case is common in a dump written as INSERT INTO `users` VALUES (…) with no column list and the schema kept in a separate file.
Can I export every table in one go?
No. Each run produces one table, and when the dump holds several the summary under the output says how many were seen. Run it again with a different name in the Table field for the next one, or clear the field to fall back to the first.
Why is NOW() sitting in one of my cells?
Because nothing is executed. Function calls, expressions and defaults are copied through as the text they were written as, so a row inserted with NOW() or CURRENT_TIMESTAMP produces that word in the CSV rather than a date. Only literal values arrive as data.
What happens to binary and hex columns?
They come across as text, not bytes. 0x1f is passed through exactly as written and X'4869' has its prefix removed to leave 4869, so a BLOB column gives you the hex, not a usable file. Extracting real binary content is a different job from this one.
Where did the backslash in my value go?
It was read as an escape. The parser follows MySQL rules everywhere, so \n becomes a newline and \t a tab no matter which database wrote the dump. A PostgreSQL string holding a literal backslash — a Windows path, a regex — therefore comes out short one character, and that column needs a look before you rely on it.
How large a dump can I paste?
Everything is held in the tab's memory and there is no upload step, so a few megabytes of statements is comfortable — around 60,000 rows parse in well under a second — and a very large backup will make the browser slow long before any limit is hit. Cut the file down to the table you need, then paste that.

Related tools

  • JSON to CSV — Flatten a JSON array into a CSV table.
  • CSV to JSON — Turn a CSV file into structured JSON.
  • CSV Viewer — Open and browse a large CSV without a spreadsheet app.
  • CSV to Excel — Convert CSV files into an XLSX workbook.
  • Excel to CSV — Export spreadsheet sheets to CSV.
  • CSV Merge — Combine multiple CSV files with matching columns.