CSV to SQL Converter

A customer export lands as a CSV and the staging table expects INSERT statements, or you are seeding a demo database from a spreadsheet someone already maintains. Hand-writing inserts for a hundred rows is how apostrophes and type mismatches sneak into production scripts. This converter parses RFC 4180 CSV in the browser, infers a column type per field, and emits a CREATE TABLE plus multi-row batched INSERTs tuned to your SQL dialect.

Paste the data, pick PostgreSQL, MySQL, SQLite, or SQL Server, and copy or download the result. Type inference, identifier sanitization, and quote escaping all happen locally, so real exports never leave the machine.

Inferred columns
—
# Column Inferred SQL type
Generated SQL
—

How the conversion pipeline works

The parser is a single-pass state machine over the raw text. It tracks whether it is inside a quoted field, treats doubled double-quotes as a literal quote character, and splits on the chosen delimiter only at the top level, so a comma inside a quoted name never becomes a column break. Carriage returns are swallowed and line feeds advance the row counter, which keeps Windows-exported files from producing empty trailing rows.

Header names are sanitized into safe identifiers: characters outside A-Z, a-z, 0-9, and underscore become underscores, a leading digit gains a c_ prefix, and duplicates are renamed with a numeric suffix while a warning records the change. The same names are then quoted with the dialect-appropriate wrapper (double quotes, backticks, or brackets) in both the CREATE TABLE and every INSERT column list.

Values are cast per inferred type: numbers are emitted bare, booleans become TRUE/FALSE (or 1/0 for MySQL and SQLite), dates and timestamps stay quoted strings, and everything else is quote-escaped for the target dialect. Rows accumulate into multi-row VALUES clauses until the batch size is reached, then a fresh INSERT statement begins, ending with a semicolon.

Before and after: a three-row export

Input — customers.csv with mixed types and an apostrophe:

id,name,email,active,signup_date
1,Alice Johnson,alice@example.com,true,2024-01-15
2,Bob O'Brien,bob@example.com,false,2024-03-01
3,Carol "CJ" Nguyen,carol@example.com,true,2024-07-04

Output — PostgreSQL dialect, CREATE + INSERT:

CREATE TABLE "customers" (
  "id" INTEGER,
  "name" TEXT,
  "email" TEXT,
  "active" BOOLEAN,
  "signup_date" DATE
);

INSERT INTO "customers" ("id", "name", "email", "active", "signup_date") VALUES
  (1, 'Alice Johnson', 'alice@example.com', TRUE, '2024-01-15'),
  (2, 'Bob O''Brien', 'bob@example.com', FALSE, '2024-03-01'),
  (3, 'Carol "CJ" Nguyen', 'carol@example.com', TRUE, '2024-07-04');

Who converts CSV to SQL

Developers

  • Seed a local development database with realistic fixture data without writing a throwaway import script every sprint.
  • Turn a vendor-supplied CSV sample into a CREATE TABLE draft that matches the real column cardinality before writing migrations.
  • Reproduce a data-shape bug by loading the exact offending export into a scratch schema and querying it directly.

Production

  • Build backfill scripts for one-off data corrections that DBAs can review as plain SQL instead of opaque binary imports.
  • Load legacy system exports into a new warehouse schema during a migration cut-over weekend.
  • Generate idempotent-looking batch inserts sized to stay under packet limits on constrained managed databases.

Workflow

  • Hand marketing a self-serve way to load a cleaned list into a staging table without SQL access or a ticket to the data team.
  • Archive a spreadsheet as versioned .sql files in the infrastructure repository so data snapshots travel with code reviews.
  • Convert government or finance CSV releases into queryable tables for ad-hoc analysis without uploading the file to a third-party service.

Common mistakes when loading CSV into databases

  • Assuming every apostrophe is already escaped. A name like O'Brien breaks any script that concatenates strings without doubling the quote. This converter escapes per dialect automatically; if you hand-edit the output, keep the doubling intact.
  • Letting Excel reformat identifiers and dates. Exported-from-spreadsheet CSV often turns 1-800 numbers into scientific notation and dates into the locale format. Check the inferred column list here before assuming the data survived the round trip through Excel.
  • Issuing one INSERT per row against a remote server. A thousand single-row inserts can take minutes over a high-latency connection. Raise or lower the batch size here until statement length sits comfortably under your server's max_allowed_packet or similar limit.
  • Trusting empty string and NULL interchangeably. An empty cell loaded as '' will not match WHERE col IS NULL later. Decide per dataset whether blanks mean missing or empty, and set the NULL toggle accordingly before generating the script.

Best practices for CSV imports

  • 1.Generate into a staging table first, validate row counts and spot-check rows, then promote with an INSERT INTO target SELECT FROM staging statement inside a transaction.
  • 2.Pin explicit column lists in every INSERT, which this tool already emits, so a later schema change does not silently shift positional values into the wrong fields.
  • 3.Keep the batch size aligned with your database packet limit; 200 rows is a safe default for most managed PostgreSQL and MySQL instances and can be raised for bulk warehouse loads.
  • 4.Store the generated .sql alongside the source .csv in version control so the exact transformation used for a load is auditable months later.

Frequently asked questions

How does the converter decide SQL column types?

Each column is sampled across every data row, then classified in a fixed order: if all non-empty values are integers it picks INTEGER, widening to BIGINT when any value exceeds the four-byte range; if any value is a decimal or scientific notation it picks DOUBLE PRECISION; then BOOLEAN, DATE (YYYY-MM-DD), TIMESTAMP, and finally TEXT as the fallback. Empty cells are ignored during inference so a partially filled column does not degrade its type, and the column list next to the output shows exactly what was chosen so you can override by editing the generated CREATE TABLE.

Which SQL dialects are supported and what differs between them?

The generator emits PostgreSQL, MySQL, SQLite, and SQL Server flavors. PostgreSQL and SQLite wrap identifiers in double quotes and boolean literals as TRUE or FALSE; MySQL uses backticks around identifiers and escapes single quotes with a backslash by default; SQL Server brackets identifiers with square quotes. String escaping follows the same dialect rules so a name containing an apostrophe becomes a valid literal in every target instead of a syntax error.

Why are INSERT statements batched instead of one per row?

A single INSERT per row turns a hundred-line CSV into a hundred round trips, which crawls on remote databases and floods replication logs. This converter groups rows into multi-row VALUES clauses (200 rows per statement by default, adjustable) so the loader executes a handful of statements instead of hundreds. The batch size is a trade-off: larger batches are faster up to the point where individual statement size or packet limits become a constraint.

How are special characters and quotes in the data handled?

The CSV parser follows RFC 4180: fields may be wrapped in double quotes, and a literal double quote inside a quoted field is escaped by doubling it. When emitting SQL, single quotes are doubled (or backslash-escaped for MySQL) so strings like O'Brien survive intact. Newlines inside quoted CSV fields are preserved in the SQL literal, which is valid in PostgreSQL and MySQL string literals and keeps multi-line addresses from shattering the statement.

Can I generate only INSERT statements without CREATE TABLE?

Yes. Switch the mode selector to INSERT only when the table already exists and you only need data-loading statements, for example when a migration tool owns the schema. In that mode the tool still infers column types to format values correctly (numbers unquoted, booleans as literals) but skips the CREATE TABLE block entirely.

What happens to empty cells and ragged rows?

By default an empty cell becomes an empty string literal in the output; flipping the empty-as-NULL switch emits the NULL keyword instead, which is the right choice for numeric and date columns where an empty string would violate the type. Rows with more or fewer fields than the header are padded or truncated to the header width, and a warning counts how many rows differed so silent data loss is visible before you run the import.

Does anything I paste leave the browser?

No. The parsing, type inference, and SQL generation all run in this page with no backend, no analytics beacon, and no fetch calls. That makes the converter appropriate for pasting real customer exports, internal financial CSVs, or any dataset that contractual obligations keep inside your own network.

Related CSV and SQL tools