CSV to SQL Script

A spreadsheet becomes the SQL, the written instructions a database understands, that loads its rows into a real table. The type of each column is worked out from what is actually in it, and you get the script to run yourself.

How to use it

Each column is judged on everything in it, and the narrowest type that still fits them all is the one chosen: whole numbers, exact decimals where money is involved, dates, true or false, or text sized to the longest value.

  1. Paste the CSV and name the table The header row becomes the column names. Tidying them lowercases everything and turns spaces into underscores, which saves quoting them forever afterwards.
  2. Choose your database The quoting characters, the type names and the way an existing row is updated are all different, so this matters more than it looks.
  3. Check the types before you run it They are listed above the script. A column of postcodes or phone numbers must be text, and if it came out as a number, the leading zeros were already lost in the spreadsheet.
Full instructions
1 gives one row each
Needed for skip and update

Your spreadsheet

paste a CSV with a header row

SQL

In depth

Turning a spreadsheet into SQL statements your database can run

Moving rows from a CSV file into a database table is one of those jobs that sounds easy until you sit down to do it. You need the right column types, the right quoting style, and the right behaviour when a row already exists. This article walks through every control on the CSV to SQL tool, shows a worked example from paste to finished script, and covers the mistakes that trip people up most often.

Screenshot: From a spreadsheet to a table in a database
From a spreadsheet to a table in a database as it appears when the page opens.

What the tool does and who needs it

The CSV to SQL Script tool reads a CSV file, works out what type each column should be, and writes a complete SQL script with a CREATE TABLE statement and batched INSERT statements. The script is ready to run in your database client. Analysts who get data as spreadsheets, developers setting up test data, and anyone migrating rows from one system to another will find it useful.

The whole process runs in your browser. Nothing is sent to a server, which matters when the spreadsheet holds customer records or financial figures. The CSV stays on your own device from start to finish.

Picking the right database

The Database dropdown offers four choices: MySQL or MariaDB, PostgreSQL, SQLite, and SQL Server. This setting controls more than you might expect. Each database uses different characters to quote column names, spells its types differently, and handles upserts with a different syntax. A script built for PostgreSQL will fail on MySQL, so get this right before you look at anything else.

MySQL wraps names in backticks, PostgreSQL and SQLite use double quotes, and SQL Server uses square brackets. The tool handles all of this for you, including reserved words like order or group that would be syntax errors without the quoting.

Naming the table and tidying the columns

The Table name field sets the name used in the CREATE TABLE and INSERT statements. It defaults to my_table, and you should change it to something meaningful before you run the script.

The Tidy the column names checkbox is on by default. When ticked, the header row is cleaned up: letters go to lowercase, and spaces become underscores. A column called First Name turns into first_name. This small step saves a lot of quoting later on, because a name without spaces and odd characters is far easier to use in every query you write afterwards. Turn it off if the column names must stay exactly as they are in the original file.

How the column types are decided

Every value in every column is examined. The tool picks the narrowest type that can hold all of them. If every value is a whole number, the column gets an integer type. If they look like money with two decimal places, it gets an exact decimal type rather than a floating point one, because floating point arithmetic drifts in ways that make totals wrong by small amounts nobody can track down. Dates become date columns, true and false values become boolean columns, and everything else falls back to text with a length that fits the longest value.

If even one value in a column does not fit the pattern, the whole column widens. A column of numbers with a single text entry in row forty becomes a text column. This is deliberate: losing data is worse than using a wider type. Check the type list above the script before you run it. A column of postcodes or phone numbers that came out as an integer means the leading zeros were already gone in the spreadsheet.

Batching, transactions and empty cells

The Rows per statement field controls how many rows go into each INSERT statement. The default of 100 is a good balance. One statement with a hundred rows loads much faster than a hundred single-row statements, because each round trip to the database has overhead. If your database complains that a statement is too large, lower this number. Setting it to 1 gives you one INSERT per row, which is the slowest but most readable option.

The Wrap the whole thing in one transaction checkbox ensures that either every row goes in or none do. If row four hundred has a problem, the entire load rolls back and the table stays as it was. For production data this is almost always what you want.

The An empty cell means dropdown has two options. Nothing at all writes NULL, which means the value is unknown. An empty piece of text writes a zero-length string, which means the value is known to be empty. The difference matters in later queries: a WHERE clause that filters for empty strings will miss NULLs, and vice versa.

Handling rows that already exist

The If the row already exists dropdown has three settings. Let the database complain is the default, and it means any duplicate key causes an error that stops the load. Skip it tells the database to ignore the new row and keep the old one. Update it performs an upsert: the row is inserted if it is new, or its other columns are overwritten if the key already exists.

Both Skip it and Update it require a Key column. Type the name of the column that has a unique index in your database. Without that index, the database has no way to know whether a row already exists, and it will simply add a second copy. The tool writes the correct upsert syntax for whichever database you chose: ON DUPLICATE KEY UPDATE for MySQL, ON CONFLICT DO UPDATE for PostgreSQL, INSERT OR REPLACE for SQLite, and a MERGE statement for SQL Server.

A worked example: loading a product catalogue

Suppose you have a CSV with four columns: sku, product_name, price, and launch_date. It has three hundred rows. Paste the CSV into the input area. Set Database to PostgreSQL, Table name to products, and leave Rows per statement at 100. Make sure Include CREATE TABLE is ticked.

The tool scans every row. The sku column is all text, so it becomes VARCHAR(12) sized to the longest SKU. The product_name column becomes VARCHAR(80). The price column has values like 29.95 and 149.00, so it becomes NUMERIC(6,2), an exact decimal that adds up correctly. The launch_date column becomes DATE. The output pane shows the CREATE TABLE followed by three INSERT statements, each holding a hundred rows, wrapped in BEGIN and COMMIT.

Now set If the row already exists to Update it and type sku in the Key column. The INSERT statements change to include ON CONFLICT (sku) DO UPDATE SET, listing every column except the key. You can run this script repeatedly as the catalogue changes, and existing products get their prices and names refreshed.

Common mistakes and when to choose something else

The most common mistake is running the script against the wrong database engine. A PostgreSQL script run in MySQL fails on the first line because the quoting characters are different. Always match the dropdown to the database you are targeting.

Another frequent problem is forgetting the key column when using the skip or update options. The script will run, but without a unique index the database just keeps adding rows. Check that the index exists before you load.

This tool writes INSERT statements. If your database supports a bulk loader, such as COPY in PostgreSQL or LOAD DATA in MySQL, that route will be faster for truly large files with millions of rows. For files up to a few hundred thousand rows, batched inserts work well and have the advantage of running in any SQL client without special permissions. Also keep in mind that the Include CREATE TABLE checkbox can be turned off if the table already exists and you only need the inserts. Leaving it on when the table is already there will produce an error on the CREATE line, so adjust this setting to match your situation.

Getting the result out

The Copy button puts the full script on your clipboard so you can paste it into a database client. The Download button saves it as a file, which is better for large scripts that might be slow to paste. Because the script is built in pieces rather than held as one string in memory, it handles files with hundreds of thousands of rows without trouble.

Everything here runs on your own machine. The CSV never leaves your browser, the types are worked out locally, and the SQL is assembled locally. Close the tab and there is nothing left behind.

Help

From a spreadsheet to a table in a database

Each column is judged on everything in it, and the narrowest type that still fits them all is the one chosen: whole numbers, exact decimals where money is involved, dates, true or false, or text sized to the longest value. Below the create statement the rows follow as inserts in batches, quoted the way your particular database expects them.

Paste the CSV and name the table

The header row becomes the column names. Tidying them lowercases everything and turns spaces into underscores, which saves quoting them forever afterwards.

Choose your database

The quoting characters, the type names and the way an existing row is updated are all different, so this matters more than it looks.

Check the types before you run it

They are listed above the script. A column of postcodes or phone numbers must be text, and if it came out as a number, the leading zeros were already lost in the spreadsheet.

Run it in one go

With the transaction option on, either the whole load succeeds or nothing changes, which is what you want when row four hundred turns out to be malformed.

Money must never be a floating point number

The obvious type for a price column looks like the one that holds decimals, and it is the wrong answer. Those types store numbers in binary, and 0.1 has no exact binary form, so adding ten of them gives 0.9999999999 rather than 1. Sum a million invoice lines that way and the total is quietly out by pennies that nobody can account for. Columns that look like money are therefore given an exact decimal type here, sized from the longest value found, which stores the number you typed and adds up the way a calculator does. The same care applies to anything with a leading zero: a postcode or an account number is text, not a number, however numeric it looks.

Good to know

Batches are much faster than single rows

One statement carrying a hundred rows loads far quicker than a hundred statements. Drop the batch size if your database complains that a statement is too large.

Empty and nothing are different things

A blank cell can mean "we know it is empty" or "we never found out". Those behave differently in every later query, so the setting is worth a moment of thought.

Update needs a real key

Skipping or updating an existing row only works if the database already has a unique index on the column you name. Without one it will simply insert a second copy.

Reserved words are quoted for you

A column called order, group or user is legal here because every name is quoted. Renaming it anyway will save the next person a bad afternoon.

Common questions

How do I import a CSV into a database?
Paste it here, choose your database, and run the script it produces. The create statement makes the table and the inserts fill it.
How are the column types decided?
By looking at every value in the column, not a sample. If a single value does not fit, the column widens to text rather than losing it.
What is an upsert?
One statement that inserts a row if it is new and updates it if it is already there. Each database spells it differently, and the right spelling is used for the one you pick.
Can I use this for a very large file?
Yes. The script is built in pieces and offered as a download rather than being held in one enormous string, so a file with hundreds of thousands of rows is fine.
Is my data uploaded to write the script?
No. The whole script is written in your browser.