EthicalFusion
Data & Developers

How to Convert CSV to SQL INSERT Statements (and When to Bulk Load)

Paste CSV with a header row and get one INSERT per row. How the converter quotes text, numbers and NULLs, its MySQL backslash gap, and faster bulk-load commands.

By the EthicalFusion team Published Updated 8 min read

To turn a CSV file into SQL, paste it (with its header row) into the free CSV to SQL converter, type the table name and click Generate SQL. You get one INSERT INTO statement per row, with the header names as columns, ready to run in MySQL or SQLite. The table must already exist, because the converter doesn't write CREATE TABLE.

That suits a few hundred rows of seed data. For thousands of rows, a bulk loader (MySQL's LOAD DATA, PostgreSQL's COPY or SQLite's .import) is much faster. This guide shows exactly what the converter writes, where it can go wrong, and the bulk-load commands.

Worked example

We pasted this CSV and typed customers as the table name:

  • id,name,city,pin,phone,balance,joined,note
  • 1,Asha Rao,Pune,411001,09876543210,1499.50,2026-10-21,
  • 2,"D'Souza, Ravi",Mumbai,400001,9876543210,-250,21/10/2026,"said ""hi"""
  • 3,Meera, Chennai,600001,,1e3,,NULL

The converter returned three statements (the column list is the same each time):

  • INSERT INTO `customers` (`id`, `name`, `city`, `pin`, `phone`, `balance`, `joined`, `note`) VALUES (1, 'Asha Rao', 'Pune', 411001, 09876543210, 1499.50, '2026-10-21', NULL);
  • ... VALUES (2, 'D''Souza, Ravi', 'Mumbai', 400001, 9876543210, -250, '21/10/2026', 'said "hi"');
  • ... VALUES (3, 'Meera', ' Chennai', 600001, NULL, '1e3', NULL, 'NULL');

We ran these in SQLite 3.49 against a table with pin and phone as TEXT columns. All three rows went in, but Asha's phone was stored as 9876543210: the leading zero was lost because the value was written as a number.

How each value is written

CSV valueSQL writtenNote
1499.50, -2501499.50, -250Digits with an optional minus sign and decimal point are left unquoted
0987654321009876543210Unquoted, so stored as 9876543210
1e3'1e3'Not treated as a number
D'Souza, Ravi'D''Souza, Ravi'Apostrophes doubled, the SQL standard escape
"said ""hi"""'said "hi"'CSV quoting removed
EmptyNULLNever an empty string
The word NULL'NULL'A four-letter string, not NULL
Chennai (space after the comma)' Chennai'Spaces are kept
21/10/2026'21/10/2026'Passed through as text

Dates are passed through unchanged, so put them in YYYY-MM-DD form before converting. MySQL's DATE type expects that format, and its default strict mode rejects '21/10/2026'. Keep phone numbers, PIN codes and IDs with leading zeros as text: either fix them after converting by adding quotes, or keep them out of the number pattern (for example +91 98765 43210 is quoted). If Excel already stripped the zeros, see why CSV opens wrong in Excel.

Table and column names

  • Table name: only letters, digits and underscores are kept. my-table becomes mytable, and dbo.users becomes dbousers, so a schema prefix is lost; add it back by hand. A blank box gives my_table.
  • Columns: the header cells, trimmed and wrapped in backticks. Nothing else is checked, so a header containing a backtick breaks the statement.
  • Row length: extra cells beyond the header are silently dropped, and missing cells become NULL.
  • Separators: commas only. A semicolon- or tab-separated file comes out as one long column.

Backticks are MySQL's identifier quotes, and SQLite accepts them for compatibility. PostgreSQL and SQL Server don't: before running the script there, replace the backticks in the table and column names with double quotes (SQL Server also accepts square brackets).

Is the generated SQL safe to run?

Doubling apostrophes is the standard way to escape SQL strings, so a name like D'Souza can't end the string early, and the table name is cleaned. Three gaps remain:

  1. Backslashes in MySQL. The converter leaves \ alone. In SQLite and PostgreSQL that's fine, and SQLite stored C:\data\ exactly. But MySQL's manual says that, unless the NO_BACKSLASH_ESCAPES SQL mode is on, a backslash starts an escape sequence, and \' means a quote character. A value ending in a backslash, such as C:\data\, therefore doesn't close its string: the statement fails, and a deliberately crafted value could inject SQL of its own. Before converting a file for MySQL, double every backslash in it (find \, replace with \\), or use LOAD DATA instead.
  2. Header names are not escaped. Read the column list before you run anything.
  3. Unquoted numbers aren't an injection risk, but they change data, as with the phone number above.

Treat any CSV that came from outside your team as untrusted. Read the output, test it on a copy of the database inside a transaction so you can ROLLBACK, and run it as a database user that can only insert into that table. In application code, never build SQL by joining strings: use parameterised queries. The converter is for one-off seeding and migrations, not for code that runs every day.

Batch size: transactions and multi-row INSERTs

The converter writes one statement per row and no transaction, so by default each row is committed separately. That is very slow. We generated 5,000 rows with the converter and ran them in SQLite on a Windows laptop: 77.5 seconds with each statement committed on its own, and 0.03 seconds with BEGIN; at the top and COMMIT; at the end. The SQLite FAQ explains why: SQLite can do 50,000 or more INSERT statements a second, but only a few dozen transactions.

  • Always wrap the script in BEGIN; … COMMIT; (in MySQL, START TRANSACTION; also works). The PostgreSQL manual gives the same advice and notes a bonus: if one row fails, everything rolls back, so you aren't left with half a table.
  • Combine rows into multi-row statements, INSERT INTO t (a, b) VALUES (1, 'x'), (2, 'y'), (3, 'z');. MySQL's manual calls this "considerably faster (many times faster in some cases)" than single-row INSERTs. Every statement must fit within MySQL's max_allowed_packet setting.
  • A practical starting point is a few hundred to a thousand rows per INSERT and one transaction per 10,000 to 50,000 rows. Smaller batches waste time on overhead; huge ones are slow to roll back and hard to retry. Measure on your own data.

Bulk-load alternatives

For anything larger than a few thousand rows, skip INSERT statements and let the database read the CSV directly.

DatabaseCommandHeader rowEmpty field becomes
MySQLLOAD DATA LOCAL INFILE 'customers.csv' INTO TABLE customers FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES;IGNORE 1 LINESEmpty string, or 0 in a number column
PostgreSQL\copy customers FROM 'customers.csv' WITH (FORMAT csv, HEADER true)HEADER trueNULL (a quoted "" is an empty string)
SQLite CLI.import --csv --skip 1 customers.csv customers--skip 1Empty string

MySQL: the manual says LOAD DATA is "usually 20 times faster than using INSERT statements". Use '\n' instead of '\r\n' for files saved on Linux or Mac. LOCAL reads the file from your computer, but the server's local_infile setting is off by default and the client must allow it too (mysql --local-infile=1). Without LOCAL, the file must be on the server, you need the FILE privilege, and the secure_file_priv setting can limit which folder it may be in. To load a NULL, write \N in the CSV.

PostgreSQL: COPY customers FROM '/path/customers.csv' WITH (FORMAT csv, HEADER true) reads a file on the database server, which needs superuser rights or the pg_read_server_files role. The psql command \copy shown in the table reads the file from your own computer instead. The manual says COPY is almost always faster than INSERT, even batched in one transaction.

SQLite: run .import in the sqlite3 command-line shell. If the table exists, every row is treated as data, so --skip 1 stops the header being inserted. If it doesn't exist, SQLite creates it from the first row, so leave out --skip 1; the new columns have no declared type, so create the table yourself when types matter. In our test with sqlite3 3.54.1, an empty field was stored as an empty string, not NULL.

Before you run the script

  1. Create the table with the right types: TEXT or VARCHAR for phone numbers, PIN codes and IDs with leading zeros.
  2. Convert dates to YYYY-MM-DD, and check that empty cells should really be NULL.
  3. For MySQL, double the backslashes; for PostgreSQL or SQL Server, change the backticks.
  4. Run the first ten statements on a test copy, then the whole file inside a transaction.
  5. Compare SELECT COUNT(*) with the number of rows the converter reported.

Starting from a spreadsheet? Export it with the Excel to CSV converter first; see how to convert Excel to CSV. Need the rows as objects for an API instead? Use the CSV to JSON converter. Building a WHERE id IN (...) list from a column of values? The list to comma-separated tool does that.

Frequently asked questions

Does the converter create the table?

No. It writes only INSERT statements. Create the table first, with column names that match your CSV header exactly.

How do I insert an empty string instead of NULL?

The converter always writes NULL for an empty cell. Replace it in the output, or set a default on the column and leave the column out of the INSERT.

Can I use the output in PostgreSQL?

Yes, after replacing the backticks round the table and column names with double quotes. For large files, \copy is faster.

Why did my leading zeros disappear?

Values made only of digits are written without quotes, so the database stores them as numbers. Add quotes to those values, or load the file with a bulk loader into a TEXT column.

Is my data uploaded?

No. The CSV to SQL converter runs in your browser, so customer data stays on your computer.

Sources: MySQL 8.4 Reference Manual (String Literals; LOAD DATA; Security Considerations for LOAD DATA LOCAL; Optimizing INSERT Statements); PostgreSQL documentation (COPY; Populating a Database); SQLite documentation (Command Line Shell; FAQ); converter output tested in SQLite 3.49 and the sqlite3 3.54.1 shell, October 2026.

  • #csv to sql
  • #csv to insert statements
  • #load data infile
  • #postgresql copy csv
  • #sqlite import csv

Tools used in this guide

Free, no sign-up, and they run in your browser.

About EthicalFusion

EthicalFusion offers 321 free online tools for money, documents, images, writing and code. This guide was written by the team that builds them. Calculators give estimates, so confirm important figures with your bank, CA or doctor.

About us Suggest a correction

Keep reading

All articles