Skip to content
AZ Tools

JSON → SQL INSERT

Turn a JSON array of objects into INSERT statements — PostgreSQL, MySQL, SQLite, or ANSI quoting, with an optional ON CONFLICT upsert tail.

SQL(3)
INSERT INTO "users" ("id", "name", "email", "verified", "joined")
VALUES
  (1, 'Alice', 'alice@example.com', TRUE, '2024-01-15'),
  (2, 'Bob', 'bob@example.com', FALSE, '2024-03-22'),
  (3, 'Carol', NULL, TRUE, '2024-06-01');

Strings are escaped by doubling single quotes; for MySQL, backslashes are doubled too.

How to use

  1. Paste a JSON array of objects (or a single object).
  2. Set the table name and SQL dialect.
  3. Toggle multi-row VALUES for a single statement, or off for one INSERT per row. Toggle upsert if you want ON CONFLICT / ON DUPLICATE KEY UPDATE.

About this tool

Show

Pastes a JSON array (or a single object) and emits a ready-to-run SQL insert. Columns are the union of all keys; rows missing a column get NULL. Strings are single-quote escaped, numbers and booleans become literals, nested objects / arrays get serialized to JSON. Optional ON CONFLICT (Postgres / SQLite) or ON DUPLICATE KEY UPDATE (MySQL) so you can drop the output straight into a sync job.

Taking the union of all keys is the decision that makes a ragged array work, and it is also where a silent surprise lives. An object that never carries a field still gets a column with NULL in it, so a typo in one record's key adds a whole column to the statement rather than failing. Scan the column list before running the output; it is the fastest way to catch a emial among five hundred rows.

String escaping here doubles the single quote, which is what SQL requires, and for MySQL, whose default mode treats a backslash as an escape, it doubles backslashes as well; that is enough for the output to be valid. It is not a substitute for parameter binding in code that takes input from anyone else. This tool is for data you already have in front of you; a statement assembled by concatenation in an application is the shape injection takes, and the fix there is placeholders rather than better escaping.

Nested structures are serialized to a JSON string, which is a reasonable default and not always what you want. Postgres will accept that string into a json or jsonb column but stores it as text in a text one, and MySQL's JSON type validates on insert. If the destination column is relational rather than document-shaped, the nested part needs flattening or a second table before this output is useful.

The upsert clauses differ by more than syntax. Postgres and SQLite need a conflict target, a column or constraint the conflict is checked against, while MySQL applies the clause to whatever unique key was violated. A statement written for one and run on the other either fails to parse or updates on a key you did not intend.

A very large array becomes a very large single statement, and most servers have a packet or statement limit well below what a browser will happily generate.

Frequently asked questions

How are nested objects handled?

Serialized to JSON and inserted as a quoted string. PostgreSQL and MySQL columns of type JSON / JSONB accept these directly; for SQLite they go in as TEXT.

What's the difference between multi-row and one INSERT per row?

Multi-row produces a single INSERT INTO ... VALUES (…), (…), (…); which runs much faster on bulk loads. Per-row produces N statements — handier when you want to comment out individual rows or expect failures to be partial.

Is the escaping enough to make the SQL safe to run?

It escapes single quotes (and, for MySQL, backslashes) so the statements are syntactically valid, which is not the same as being safe against hostile input. Generated SQL is just text: read it before you run it, and when the data comes from somewhere you do not control, load it with parameterised statements or a bulk loader such as COPY or LOAD DATA instead of pasting a generated script into a console.

Why did my numbers come out quoted, or my ids lose digits?

Types follow the JSON, not your table. A value written as "42" in JSON emits a quoted literal even if the column is an integer, so cast it in SQL or fix the JSON. Large identifiers are the opposite trap: if they arrived as JSON numbers they may already have lost their last digits before reaching this tool, which is why ids should travel as strings.

Related tools