Home CSV to SQL
CSV to SQL converter
Turn a CSV file into a CREATE TABLE and a batched INSERT, with column types read from the data rather than guessed as TEXT. Runs in this tab; nothing is uploaded.
How column types are inferred
A converter that emits forty columns of TEXT has not saved you any work. This
one reads every value in a column and picks the narrowest type that fits all of them. Nulls
and empty cells are ignored when deciding, so one blank row does not push a numeric column
back to text.
| When every value is… | Column type |
|---|---|
| true / false | BOOLEAN |
| a whole number, at most 15 digits | BIGINT |
| numeric with a decimal point | DECIMAL(18,6) |
| YYYY-MM-DD | DATE |
| YYYY-MM-DD followed by a time | TIMESTAMP |
| a UUID | UUID |
| text, longest value ≤ 255 | VARCHAR(n) |
| text, longest value > 255 | TEXT |
| all empty | TEXT |
Why VARCHAR widths are rounded up
A column whose longest value is 41 characters does not become VARCHAR(41). It
becomes VARCHAR(64): the width is the longest value plus half again, rounded up
to a multiple of 16 and capped at 255. Sizing a column to the sample is the mistake that
makes the schema work perfectly on the file you converted and reject the next one — the
sample tells you the order of magnitude, not the limit.
Quoting and injection
Identifiers are quoted for the dialect you pick — "double quotes" for
PostgreSQL, `backticks` for MySQL — so a column called order or
group does not collide with a keyword. Single quotes inside values are doubled,
which is the escaping that matters here: the output of this page is, by definition, text
someone is about to paste into a database console.
What it produces
One CREATE TABLE followed by a single multi-row INSERT. A batched
insert is dramatically faster than one statement per row — on most engines the difference is
an order of magnitude on a few thousand rows, because it is one parse and one transaction
rather than thousands.
Empty cells become NULL, not ''. If your loader needs the opposite,
turn type detection off and the empty strings come through literally.
Before you run it
- There is no primary key. Nothing in a CSV file says which column is one. Add the constraint yourself, or add an identity column.
- There are no indexes. Add them after the insert, not before — building an index while loading is slower than building it once at the end.
- Dates are not validated. A column that looks like a date is typed as one; a row with
2024-02-31in it will be rejected by the database, which is the right place for that to fail. - Very large files belong in a bulk loader. Past a few thousand rows, COPY or LOAD DATA will beat any INSERT.
Common questions
Which SQL dialects are supported?
PostgreSQL and MySQL. The difference is identifier quoting — double quotes versus backticks — and both outputs are close enough to standard SQL to run on SQLite and SQL Server with small edits.
Does it create a primary key?
No. Nothing in a CSV file identifies which column is the key, so guessing would be wrong as often as it was right. Add the constraint after the CREATE TABLE, or add an identity column.
How are column types chosen?
By reading every value in the column and picking the narrowest type all of them fit — boolean, bigint, decimal, date, timestamp, uuid, varchar or text. Empty cells are ignored when deciding, so one blank row does not force a numeric column to text.
Are values escaped safely?
Single quotes inside values are doubled, which is the SQL string escape. Identifiers are quoted for the dialect, so a column named order or group does not collide with a keyword.
Is the file uploaded to a server?
No. Everything runs in your browser, which matters more here than usual — the CSV files people convert to SQL tend to be the ones they cannot paste into a hosted tool.
Related
- /sql — generating SQL inserts from a schema instead of converting a file.
- /postgresql-test-data — COPY, sequences and deferred constraints when the insert gets large.
- /mysql-test-data — LOAD DATA, utf8mb4 and AUTO_INCREMENT notes.
- /json-to-sql-converter — the same output, starting from JSON.
Last updated