Browser processing Input is processed on your device. Privacy & storage

CSV to SQL

CSV in, a CREATE TABLE plus batched INSERT statements out — with types inferred from the data itself.

CSV input
File: No file chosen
SQL output

How column types are inferred

Every non-empty cell in a column is inspected, and the column gets the narrowest type that describes all of them. A single value that does not fit demotes the whole column, which is why checking a sample of your data before trusting the output is worthwhile.

Every non-empty cell looks likePostgreSQLMySQLSQLite
a whole numberINTEGER or BIGINTINT or BIGINTINTEGER
a decimal numberNUMERICDECIMAL(20,6)REAL
true / falseBOOLEANTINYINT(1)INTEGER
YYYY-MM-DDDATEDATETEXT
a date and timeTIMESTAMPTZDATETIMETEXT
a UUIDUUIDCHAR(36)TEXT
anything elseTEXTVARCHAR(n) or TEXTTEXT

n is the longest value seen in that column, so a VARCHAR sized from the sample can still be too short for a row that arrives later. Uncheck Infer types if you would rather have every column as TEXT and cast yourself.

NOT NULL comes from the data, not from a guess

A column that never has an empty cell is declared NOT NULL; one that does is left nullable. With Empty cell → NULL switched off, an empty cell becomes an empty string and the column is nullable either way.

Escaping, because CSV is hostile input

A value like O'Brien would terminate the string literal early and produce a syntax error at best. Single quotes are therefore doubled, and for MySQL the backslash is escaped as well, since it is an escape character there but not in the standard. Identifiers are quoted too — backticks for MySQL, double quotes elsewhere — so a column named order or group works without hand-editing.

INSERT INTO "my_table" ("id", "name", "note") VALUES
  (1, 'Ada Lovelace', NULL),
  (2, 'O''Brien', 'a "quoted" note');

Batching

Rows are grouped into a single multi-row INSERT of up to Rows per INSERT tuples. That is dramatically faster than one statement per row, but a very large batch can hit a driver's packet limit — 100 is a safe default, lower it for rows with large text columns.

Edge cases worth knowing

  • Not a migration tool. The output is plain SQL with no transaction wrapper or ON CONFLICT handling. Wrap it yourself if you need it to be atomic.
  • For very large files, use the bulk loader. COPY in PostgreSQL and LOAD DATA INFILE in MySQL are an order of magnitude faster than INSERTs — this tool is for the sizes where a paste is still reasonable.
  • Numeric-looking text stays text. If any cell in a column is non-numeric, the whole column becomes TEXT, so a stray "n/a" cannot silently drop your numbers.
  • Reserved words are safe by default because every identifier is quoted. Turn that off in your head, not here — the generator always quotes.

FAQ

Does it generate ON CONFLICT or upserts?

No. Conflicts are a policy decision — whether to skip, update or fail depends on the table. The output is deliberately a plain insert so you can add the clause that fits.

Can I import the .sql into phpMyAdmin or psql?

Yes. The output is a plain script of statements, so psql -f file.sql or a paste into phpMyAdmin both work. Choose the dialect first, though — the quoting differs.

What happens to duplicate column names?

Headers are de-duplicated by appending _2, _3 and so on, so the CREATE TABLE never fails on a repeated name.

Is my CSV uploaded?

No. Parsing and generation run entirely in your browser.