Skip to main content
L
Loopaloo
Buy Us a Coffee
All ToolsImage ProcessingAudio ProcessingVideo ProcessingDocument & TextPDF ToolsCSV & Data AnalysisConverters & EncodersWeb ToolsMath & ScienceGames
Guides & BlogAboutContact
Buy Us a Coffee
L
Loopaloo

Free online tools for developers, designers, and content creators. Your files are processed in your browser and are never uploaded - no accounts required. A few network utilities (like What's My IP and Currency Converter) call public APIs to do their job and say so on their pages.

support@loopaloo.com

Tool Categories

  • Image Tools
  • Audio Tools
  • Video Tools
  • Document & Text
  • PDF Tools
  • CSV & Data
  • Converters
  • Web Tools
  • Math & Science
  • Games

Company

  • About Us
  • Contact
  • Blog
  • FAQ

Legal

  • Privacy Policy
  • Terms of Service
  • Disclaimer

Support

Buy Us a Coffee

© 2026 Loopaloo. All rights reserved. Built with privacy in mind.

Privacy|Terms|Disclaimer
  1. Home
  2. CSV & Data Analysis
  3. CSV to SQL
Add to favorites

CSV to SQL

Generate SQL that runs: identifiers quoted rather than stripped, values escaped per dialect, and every change to your data reported

Rows never leave your deviceMore csv & data analysisJump to full guide

Related reading

  • SQL Formatting and Best Practices: Writing Readable, Maintainable Queries10 min read

Initializing in your browser…

You might also like

CSV Viewer & Editor

View, sort and edit a CSV, with an undo stack that goes back exactly one step and a parse report that tells a broken quote from a ragged row

CSV to JSON Converter

Convert both ways, converting a type only when it reads back the same, and naming every value that cannot

CSV to Excel Converter

Convert CSV to XLSX or ODS with cell types that keep leading zeros, long IDs and dates intact

A sample run

You need to load an export into PostgreSQL, and it has a column called "order", a name with an apostrophe, a Windows path and a value that is just padding.

Input

id,order,customer name
1,O'Brien & Co,"  padded  "
2,C:\share\x,NULL
What CSV to SQL produces

PostgreSQL

CREATE TABLE "orders" ("id" TEXT, "order" TEXT, "customer name" TEXT);
INSERT INTO "orders" ("id", "order", "customer name")
  VALUES ('1', 'O''Brien & Co', '  padded  ');
INSERT INTO "orders" ("id", "order", "customer name")
  VALUES ('2', 'C:\share\x', 'NULL');

The reserved word "order" and the name with a space both survive because every identifier is quoted rather than stripped. The apostrophe is doubled, the padding is kept, and the four letters NULL stay text rather than becoming a missing value. Switch to MySQL and the same run writes 'C:\\share\\x' instead, because MySQL treats a backslash as an escape character and PostgreSQL does not. The whole thing is verified by executing it in SQLite and comparing the table to the CSV.

SQL That Runs, Holding the Data the CSV Held

Turn a CSV into CREATE TABLE and INSERT statements for MySQL, PostgreSQL, SQLite, SQL Server or Oracle. The test for this tool is not that the escaping looks right: it is that SQLite executes the generated script and the resulting table contains the same strings the CSV did, cell for cell, over a corpus of thirty deliberately awkward values.

How to use

  1. 1Upload your CSV file, or load the sample
  2. 2Choose the dialect, the statement types and the table name
  3. 3Set whether an empty field should become NULL or an empty string, and name any other text to treat as NULL
  4. 4Adjust the detected column types if you want to
  5. 5Click Generate SQL, read the panel of anything that needed changing, then copy or download the script

Key features

  • Verified by execution: SQLite runs the generated script and the table is compared to the CSV cell by cell
  • MySQL, PostgreSQL, SQLite, SQL Server and Oracle, each with its own escaping rules rather than one shared guess
  • Backslashes doubled for MySQL, where a backslash is an escape character, and left alone everywhere else
  • Identifiers quoted with the delimiter doubled, so select, order, first name and a name holding a quote all work
  • Nothing stripped from a column name; anything that has to change is renamed and reported with the reason
  • Per-dialect identifier length limits, including PostgreSQL truncating to 63 bytes silently
  • Per-dialect case rules, so ID and id stay two columns in PostgreSQL and are renamed for SQLite and MySQL
  • Values written exactly as the cell holds them: whitespace kept, the letters NULL kept
  • Empty fields become NULL or an empty string, your choice, plus any text you name as a null marker
  • Numbers written as numbers, so a bare 0x1F or Infinity token never reaches the database
  • A value that is not a boolean written as NULL rather than guessed as false
  • NUL bytes removed and reported, rather than producing a script that cannot be loaded
  • CREATE TABLE, INSERT (single, batched or multi-row), UPDATE, and UPSERT where the dialect supports it
  • A panel listing everything about the script that is not a faithful copy of the CSV

How it works

Generating SQL is easy to do almost correctly, and almost correctly is the worst outcome, because the script runs and the data is quietly different. Measured against the previous version of this tool on 2026-09-01, over thirty awkward values and fifteen awkward column names:

Six of the thirty values came back changed. Leading and trailing whitespace was trimmed away, so " padded" arrived as "padded". A cell holding a single space became NULL. An empty field became NULL with no way to say otherwise. And the four-letter strings NULL and null became SQL NULL, so a genuine piece of text turned into a missing value. Every one of those is silent: the script runs, no error appears, and the difference only shows up later.

The CREATE TABLE did not run at all. Column names were made safe by replacing every character outside A to Z, 0 to 9 and underscore with an underscore, which turned five different headers into the same name and made SQLite refuse the statement with "duplicate column name". A header with an accent became mojibake and an empty header became an empty quoted identifier.

And the MySQL output did not escape backslashes. That one is worth stating precisely, because it is not merely wrong. In the SQL standard, and therefore in SQLite, in PostgreSQL with standard_conforming_strings on, in SQL Server and in Oracle, a backslash inside a string literal is an ordinary character. In MySQL, unless NO_BACKSLASH_ESCAPES is set (it is not by default), a backslash starts an escape sequence. A value of C:\path\to\file written with only the quote doubled is read by MySQL as C:path, a tab, o, a form feed, ile. A value containing a backslash immediately before a quote ends the literal early, which breaks the statement and is the mechanism by which a quote escapes a string.

This version fixes all of it at the source. Values are written exactly as the cell holds them, with no trimming and no guessing: a space is a space, and the letters NULL are the letters NULL unless you say otherwise. Whether an empty field becomes NULL or an empty string is a setting rather than an assumption, and you can name additional text to treat as NULL. Identifiers are always quoted, with the delimiter escaped by doubling it, which is what makes select, order, first name, 2nd_place and a name containing a quote all work without a reserved-word list. Nothing is stripped: an awkward character in a header is a reason to quote the identifier, not a reason to lose the name.

Three things quoting cannot fix are handled explicitly and reported. Names longer than the server accepts are shortened (PostgreSQL truncates to 63 bytes silently, which makes two long names that share a prefix collide inside the database rather than in the script). Names that collide are renamed, using each server's own comparison rule: PostgreSQL and Oracle compare quoted identifiers case-sensitively so "ID" and "id" are two real columns, while SQLite, MySQL and SQL Server treat them as one. And an empty header gets a name, because no dialect allows an empty identifier.

Values destined for a numeric column go through a decimal grammar rather than through JavaScript's Number(), so 0x1F is never emitted as a bare unquoted token and a grouped 1,234 is written as 1234 rather than having its comma silently dropped. A value that is not a boolean is written as NULL rather than being guessed as false. A NUL byte, which no dialect can carry in an ordinary string literal, is removed and reported instead of producing a script most clients cannot even load. Anything that could not be carried faithfully appears in a panel above the SQL with the row count.

Where this fits a data pipeline

  • Seeding a database from a spreadsheet export

    Get a script that runs on the first try, with the identifiers quoted and the awkward values escaped for the dialect you are actually using.

  • Migrating data that has apostrophes and backslashes in it

    Names like O'Brien and Windows paths are exactly where naive generators break or corrupt data. Both are handled per dialect and verified by running the result.

  • Loading a file whose headers were never meant to be column names

    Headers with spaces, dots, dashes, reserved words or accents keep their names instead of collapsing into each other and failing the CREATE TABLE.

  • Checking what an import will do to your data before you run it

    The warnings panel lists every value that could not be carried faithfully and every column that had to be renamed, before anything reaches a server.

Frequently asked questions

How do you know the generated SQL is correct?

By running it. The test suite executes the script in SQLite and compares the resulting table to the CSV as Python's own csv module reads it, over a corpus of thirty awkward values and fifteen awkward column names. For the four dialects that are not installed, each literal is read back with a lexer written from that vendor's documentation, and that lexer is validated first by requiring it to agree with real SQLite.

Why is the MySQL output different from the PostgreSQL output?

Because MySQL reads string literals differently. Unless NO_BACKSLASH_ESCAPES is set, which is not the default, a backslash in a MySQL literal starts an escape sequence, so backslashes have to be doubled. In PostgreSQL, SQLite, SQL Server and Oracle a backslash is an ordinary character and doubling it would insert a second one. Getting this wrong in either direction corrupts data.

What happens to a column called "select" or "first name"?

It keeps its name. Every identifier is quoted, with the delimiter escaped by doubling it, which is what makes reserved words and spaces work. Nothing is stripped from a header; if a name genuinely has to change (it is empty, too long for the server, or collides with another under that server's comparison rule) it is renamed and the reason is shown.

Does an empty cell become NULL or an empty string?

Whichever you choose. Only a genuinely empty field is affected: a cell holding a space is written as a space, and a cell holding the four letters NULL is written as that text, unless you name it as a null marker.

Why did a value in my numeric column become NULL?

Because it is not a number, and the alternative is worse. A cell reading 0x1F is a string in a CSV even though JavaScript reads it as 31, and emitting the bare token 0x1F would be a blob literal in SQLite and a syntax error in PostgreSQL. Anything refused this way is listed in the warnings panel with the value and the reason.

Which dialects are supported?

MySQL, PostgreSQL, SQLite, SQL Server and Oracle. Each carries its own identifier delimiter, identifier length limit, identifier comparison rule, backslash behaviour, boolean literals, and whether it accepts a multi-row INSERT. Oracle does not, so it always gets one statement per row.

Further reading

  • SQL Formatting and Best Practices: Writing Readable, Maintainable Queries10 min read

Private by design

Rows and columns are parsed and transformed in memory in your browser. No record ever reaches a server.