Skip to main content

JSON to SQL converter

Turn a JSON array of objects into CREATE TABLE plus INSERT statements. Nested objects and arrays are stored as JSON text; every other key gets an inferred column type.

Parsed and written in this tab. No database is contacted, nothing is executed, and no row is uploaded.

JSON already has types — a number is a number and true is a boolean — which makes it a better starting point for SQL than a CSV. What it does not have is a fixed shape: one object in an array can carry a key the next one lacks, and any value can itself be an object or an array. This page resolves both. It takes the union of every key across every object, in first-seen order, so the table has a column for each; it infers a database type per column from the real values; and it stores anything nested as JSON text rather than pretending a table can hold a tree. The output is a CREATE TABLE plus batched INSERTs in MySQL, PostgreSQL or SQLite syntax.

Key facts about JSON to SQL converter

Key facts about JSON to SQL converter
Accepted shapesAn array of objects, a single object, an object with an array of objects inside it, or NDJSON/JSON Lines with one object per line
Column discoveryThe union of every key across every object, in the order the keys are first seen — a key missing from some objects becomes NULL there
Nested valuesAn object or array value is JSON.stringified into a text column; it is not flattened into extra columns and not dropped
Native JSON typesA real JSON number becomes INT or DOUBLE, a real true/false becomes the dialect's boolean, and null becomes NULL — no string parsing needed
DialectsMySQL (backtick identifiers), PostgreSQL and SQLite (double-quoted) — with the right type names and boolean literals for each
TimestampsAn ISO string like 2026-01-02T09:30:00Z is recognised as a timestamp column: DATETIME on MySQL, TIMESTAMP on PostgreSQL, TEXT on SQLite
Big integersValues past 2,147,483,647 promote the column to BIGINT instead of overflowing INT
EscapingSingle quotes are doubled in every dialect; MySQL additionally gets its backslashes doubled, which matters for stringified JSON full of them
Batching1, 50, 100 or 500 rows per INSERT statement
NOT NULLOnly on columns where every object supplies a value — a key that is sometimes absent is left nullable
Key sanitisingA key like "order total" or "2026-total" becomes a valid identifier, and every column name stays editable before export
Input ceiling10 MB of JSON text, held in memory along with the generated SQL

What happens to your file

The JSON is parsed by the browser's own JSON.parse in this tab, the SQL is built as a string in page memory, and the download is a Blob handed to a temporary object URL. No database connection exists anywhere in this page — it writes statements, it never runs them — and no API receives your objects. That is worth stating precisely because JSON exports are often API responses containing customer records: nothing here transmits them, nothing is written to localStorage, and reloading the page clears everything. You can disconnect from the network after the page has loaded and every part of the conversion still works.

About this tool

  1. 1

    Paste or load the JSON

    An array of objects is the normal case. A single object becomes a one-row table, an object wrapping an array is unwrapped, and JSON Lines is detected automatically.

  2. 2

    Pick the dialect

    MySQL, PostgreSQL or SQLite. This decides identifier quoting, type names, boolean literals and transaction keywords, so choose it before reviewing the types.

  3. 3

    Check the column list

    Every key found across every object is listed with the type inferred for it. Nested objects and arrays show as text columns — that is intentional, and the value stored is the JSON for that subtree.

  4. 4

    Rename what needs renaming

    Keys with spaces, dots or leading digits are sanitised into valid identifiers. Each name is an input field, so you can match an existing schema exactly.

  5. 5

    Set the statement options

    CREATE TABLE and IF NOT EXISTS are on by default. Add an auto-increment key when your objects have no id, DROP TABLE for a clean re-run, and a transaction wrap so a bad row rolls the whole load back.

  6. 6

    Export

    Copy the SQL into your client or download the .sql and run it with mysql, psql or sqlite3. Batch size 100 is a good default.

Specs & compatibility
Input.json, .ndjson, .jsonl or .txt, or JSON pasted into the box; up to 10 MB
OutputA .sql text file, or the same text copied to the clipboard
ParserThe browser's native JSON.parse, with a per-line fallback so JSON Lines works without a separate mode
Statement shapeCREATE TABLE [IF NOT EXISTS], optional DROP TABLE IF EXISTS first, then INSERT INTO … VALUES with one tuple per object
Transaction wrappingOptional — START TRANSACTION/COMMIT on MySQL, BEGIN/COMMIT on PostgreSQL and SQLite
PreviewThe first 20,000 characters render in the page; the download and clipboard carry all of it
Error handlingInvalid JSON is reported with the parser's own message and position rather than silently producing an empty table
Browser supportChrome, Edge, Firefox and Safari; the copy button needs a secure context
  • If your API wraps results in { "data": [ … ] }, paste the whole response — the array inside is found and used.
  • Nested arrays that you actually want as rows belong in a second table; extract them in a script first, then convert each level separately here.
  • JSON numbers keep their type, so an id field arrives as INT without the leading-zero ambiguity a CSV has.
  • A key that appears in only some objects becomes a nullable column — check the NOT NULL column in the type table if you expected a constraint.
  • Stringified JSON in a MySQL text column is exactly where backslash escaping goes wrong in hand-written SQL; it is doubled here for MySQL only, as that engine requires.
  • For repeated imports, keep DROP TABLE on and the transaction wrap on: the load is then idempotent and all-or-nothing.
  • Array, single object, wrapped array and NDJSON input
  • Union-of-keys column discovery across ragged objects
  • Nested objects and arrays preserved as JSON text
  • MySQL, PostgreSQL and SQLite with dialect-correct types and escaping
  • Batched multi-row INSERTs with optional transaction wrapping
  • Editable column names and a live SQL preview
  • Loading an API response into a database so you can query it with SQL instead of scripting over the JSON.
  • Seeding a development or test database from a fixture file that already lives in the repo.
  • Migrating a document-store export into a relational schema, one collection at a time.
  • Turning a webhook log in JSON Lines into a table for analysis.
  • Producing a migration file that a reviewer can read as SQL rather than as a data blob.
  • Getting a quick SQLite table out of a JSON dump for local exploration.
They are stored as JSON text in a single column, with the original structure intact. A relational table has no way to hold a tree in one cell, and the two alternatives are both worse: flattening a nested object into extra columns invents a schema that the rest of your data may not match, and dropping the key loses information silently. Keeping the JSON string means nothing is lost and you can expand it later — MySQL and PostgreSQL both have JSON functions that read such a column directly.
Yes. The column list is the union of every key across every object, in the order the keys are first seen, so an object missing a key simply gets NULL in that column. Columns built this way are left nullable — the NOT NULL option only applies to columns where every object supplied a value, so the generated schema never asserts a constraint your own data breaks.
Usually, yes. If the response is a bare array of objects it works as-is. If it is wrapped — the common { "data": [ … ] } or { "results": [ … ] } shape — the wrapper is unwrapped and the array inside is used. A single object becomes a one-row table. JSON Lines, where each line is its own object, is detected when the whole text does not parse as one JSON value.
The one you will run the file against, because the differences are substantive. MySQL uses backticks, INT, TINYINT(1) for booleans and DATETIME, and treats backslashes inside strings as escapes. PostgreSQL uses double quotes, INTEGER, real TRUE/FALSE and TIMESTAMP. SQLite uses double quotes and has only INTEGER, REAL and TEXT, so timestamps land as text. Generating MySQL syntax and running it on PostgreSQL usually fails outright, which is the good case — the bad case is SQL that runs and stores the wrong value.
Neither. This page parses JSON and writes text; it has no database connection and no upload endpoint. The SQL appears in a preview and downloads as a local file, and you decide where to run it. Nothing is stored in the browser between sessions either, so a reload clears the parsed objects.
The error shown is the browser's own parser message, including the position it gave up at, so start there. The usual causes are a trailing comma after the last element, single quotes instead of double quotes around keys or strings, an unquoted key, or a log file that is several JSON values concatenated without being an array — that last one works if each value is on its own line, since it is then valid JSON Lines. Running the text through a JSON formatter first will point at the exact character.
View all

Updated

We use anonymous analytics to improve ToolChamp. No personal data is stored or sold. Privacy Policy