CSV to SQL INSERT

Turn a CSV export into SQL INSERT statements, with single quotes escaped and empty cells written as NULL. Tick Add CREATE TABLE to get a draft table definition as well. Use it to seed a dev database or run a one-off fix. Semicolon, tab and pipe separated files convert the same way once you pick the matching Separator.

CSV Input
SQL Output

1 CSV to SQL INSERT example

CSV

1
2
3
4
id,name,email,active,signed_up,notes
1,Alex Morgan,alex@example.com,true,2026-01-14,VIP customer
2,Sam Patel,sam@example.com,false,2026-02-03,
3,Riley O'Brien,riley@example.com,true,2026-03-21,"Referred by Alex, follow up"

SQL

1
2
3
4
5
INSERT INTO users (id, name, email, active, signed_up, notes) VALUES
  (1, 'Alex Morgan', 'alex@example.com', TRUE, '2026-01-14', 'VIP customer'),
  (2, 'Sam Patel', 'sam@example.com', FALSE, '2026-02-03', NULL),
  (3, 'Riley O''Brien', 'riley@example.com', TRUE, '2026-03-21', 'Referred by Alex, follow up');

2 How values are escaped and typed

Single quotes are doubled

O'Brien becomes 'O''Brien'. Doubling is the standard SQL escape and works in every dialect. The tool does not use backslash escaping, which is a MySQL extension.

Empty cells become NULL

An empty field is written as NULL, not as an empty string, whether or not Detect types is on. If a column needs '' instead, replace NULL in the output for that column.

Numbers stay unquoted

A value that parses cleanly as a number is written without quotes. Turn Detect types off for zip codes, phone numbers and IDs with leading zeros, so the database stores 007 as text instead of the number 7.

Booleans map to literals

With Detect types on, true/false and yes/no become TRUE and FALSE, and a cell that says null becomes NULL. A 1 or 0 stays a number. MySQL stores TRUE and FALSE as 1 and 0 anyway.

3 Choose one statement or one per row

Use one multi-row INSERT for a clean bulk load. The database parses one statement and commits once, instead of 1,000 times for 1,000 rows. However, it is all or nothing: one bad row rolls back the whole statement. One per row is slower, but you can re-run only the rows that failed, which helps during a messy migration.

For a production import, use your database's own loader. That means COPY in PostgreSQL, LOAD DATA INFILE in MySQL and .import in SQLite. They run an order of magnitude faster on large files and never build SQL from data. This page is for seeding a dev database and one-off fixes.

How to use the CSV to SQL INSERT tool

  1. Paste CSV with a header row, or load a file with Upload CSV.
  2. Set Table to the destination table name and pick the Separator your file uses.
  3. In Statements, choose One multi-row for speed, or One per row when you expect some rows to fail.
  4. Detect types leaves numbers unquoted and writes true/false as TRUE and FALSE. Turn it off for zip codes and IDs with leading zeros.
  5. Tick Add CREATE TABLE for a draft schema with types guessed from the data, then click Copy Result or Download.
  6. Check the count above SQL Output after pasting. One column means the Separator does not match the file, so switch it to Semicolon, Tab or Pipe.

How to load a CSV into a database properly

The database's own loader (fastest)

-- PostgreSQL: reads the file server-side
COPY users (id, name, email, active)
FROM '/path/to/users.csv'
WITH (FORMAT csv, HEADER true);

-- psql, when the file is on your machine rather than the server
\copy users FROM 'users.csv' WITH (FORMAT csv, HEADER true)

-- MySQL
LOAD DATA LOCAL INFILE 'users.csv'
INTO TABLE users
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;

-- SQLite
.mode csv
.import --skip 1 users.csv users

On a large file these loaders run an order of magnitude faster than generated INSERTs, and they never build SQL from data.

Python, parameterized

import csv
import psycopg

with open("users.csv", newline="", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))

with psycopg.connect("postgresql://localhost/app") as conn:
    with conn.cursor() as cur:
        # executemany sends the values separately from the SQL:
        # nothing in the data can be read as SQL
        cur.executemany(
            "INSERT INTO users (id, name, email) VALUES (%s, %s, %s)",
            [(r["id"], r["name"], r["email"]) for r in rows],
        )
    conn.commit()

PHP, parameterized

<?php
$pdo = new PDO('pgsql:host=localhost;dbname=app', $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

$stmt = $pdo->prepare('INSERT INTO users (id, name, email) VALUES (?, ?, ?)');

$fh = fopen('users.csv', 'r');
fgetcsv($fh); // skip the header

$pdo->beginTransaction();
while (($row = fgetcsv($fh)) !== false) {
    $stmt->execute([$row[0], $row[1], $row[2]]);
}
$pdo->commit();   // one transaction is far faster than one per row
fclose($fh);

Common errors and how to fix them

syntax error at or near the middle of a value
An apostrophe was not escaped. This tool doubles every one, so if you built the SQL another way, look there first. The same gap is how SQL injection works.
Leading zeros disappeared from a zip code or ID
Detect types read 007 as the number 7. Turn Detect types off so every value is quoted as text.
The whole import fails because of one bad row
A multi-row INSERT is one statement, so it rolls back entirely. Switch Statements to One per row to isolate the failures.
Dates are rejected by the database
The CSV dates are probably not ISO 8601. Convert them to YYYY-MM-DD first, or cast in SQL with TO_DATE (Postgres) or STR_TO_DATE (MySQL).
Accented characters arrive mangled
The file or the connection is not UTF-8. Convert the file with iconv, and on MySQL check that both the table and the connection use utf8mb4.
The INSERT has a single column with a long underscored name like id_name_email
The Separator does not match the file, so the whole header became one column and the punctuation was replaced with underscores. The count above SQL Output reads 1 column. Switch Separator to the character the file uses.

Good to know

The generated CREATE TABLE is a draft. It guesses one type per column from the values in the file, and it has no information about primary keys, foreign keys, indexes, defaults or the right VARCHAR width. For example, a name column whose longest value is 38 characters becomes VARCHAR(50). Read it and fix it before you run it.

Specifications and references

This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.

Frequently Asked Questions

How are quotes in the data escaped?

Each single quote is doubled, so O'Brien becomes 'O''Brien'. That is the standard SQL escape and works in every dialect. The tool does not use backslash escaping, which is a MySQL extension.

Does it detect numbers automatically?

Yes. With Detect types on, a value that parses cleanly as a number is written unquoted, and true/false or yes/no become TRUE and FALSE. Turn it off to quote every value as a string, which keeps the leading zeros on zip codes and phone numbers. Empty cells become NULL either way.

One statement per row, or one big one?

Pick either in the Statements menu. A multi-row INSERT loads much faster because the database parses and commits once instead of once per row. However, one bad row fails the whole statement, so use One per row when you expect to re-run failures.

Can it create the table too?

Yes. Tick Add CREATE TABLE and the tool emits one with a type guessed per column: INTEGER, DECIMAL, BOOLEAN, DATE or VARCHAR. Treat it as a draft, since the data says nothing about primary keys, indexes, constraints or the right VARCHAR width.

Is this safe for untrusted data?

The escaping is correct, but a production import should not build SQL from data at all. Use your database's own loader (COPY in Postgres, LOAD DATA INFILE in MySQL) or a parameterized prepared statement. This tool is for seeding a dev database and one-off migrations.

How do I convert a semicolon-separated CSV to SQL?

Choose Semicolon in the Separator menu before pasting. Excel writes semicolons in locales that use a comma for decimals, which is why so many European exports look like this. There is no auto-detection here, so the menu has to match the file. Quoted fields, doubled quotes and line breaks inside quotes are handled identically whichever separator you pick.

Can it read a pipe or tab separated file?

Yes. Pick Pipe or Tab under Separator and the file is parsed on that character instead of the comma. Tab works for a .tsv export or a block copied from a spreadsheet, and pipe for database dumps. The table name, type detection and CREATE TABLE options behave the same, and the count above SQL Output confirms the column number is right.

Related

To read the generated SQL with proper indentation, run it through the SQL formatter. For other destinations, the same CSV converts to JSON, a PHP array or a table.

More Data Converters

Browse all data converters

Copied to clipboard!