Why Convert JSON to SQL?
Every backend engineer eventually needs to seed a database from a JSON file: a one-off import of config rows, a dataset dump from a third-party API, a fixtures file for tests, or a bulk migration from a NoSQL store. Writing the INSERTs by hand is error-prone — one missed quote and the whole script fails halfway. Letting the SQL ORM handle it means booting the full app just to run a script. A pure converter is the shortest path: paste the JSON, copy the SQL, run it.
Example: Flat Array to INSERTs
Given this JSON:
[
{ "id": 1, "name": "Ada Lovelace", "email": "[email protected]", "active": true, "signed_up_at": "2026-10-10" },
{ "id": 2, "name": "Linus Torvalds", "email": "[email protected]", "active": true, "signed_up_at": "2026-10-09" },
{ "id": 3, "name": "Donald Knuth", "email": null, "active": false, "signed_up_at": "2026-10-08" }
]Single-row mode (PostgreSQL dialect):
INSERT INTO "users" ("id", "name", "email", "active", "signed_up_at")
VALUES (1, 'Ada Lovelace', '[email protected]', TRUE, '2026-10-10');
INSERT INTO "users" ("id", "name", "email", "active", "signed_up_at")
VALUES (2, 'Linus Torvalds', '[email protected]', TRUE, '2026-10-09');
INSERT INTO "users" ("id", "name", "email", "active", "signed_up_at")
VALUES (3, 'Donald Knuth', NULL, FALSE, '2026-10-08');Bulk mode — the same data as one statement:
INSERT INTO "users" ("id", "name", "email", "active", "signed_up_at")
VALUES
(1, 'Ada Lovelace', '[email protected]', TRUE, '2026-10-10'),
(2, 'Linus Torvalds', '[email protected]', TRUE, '2026-10-09'),
(3, 'Donald Knuth', NULL, FALSE, '2026-10-08');SQL Dialect Differences That Matter
The ANSI SQL spec says identifiers go in double quotes and strings go in single quotes. MySQL and SQL Server don't fully agree.
- MySQL — identifiers in backticks:
`users`.`id`. Booleans are tinyints: emit1/0notTRUE/FALSE(TRUE/FALSE do work as aliases, but 1/0 is portable). - PostgreSQL — identifiers in double quotes:
"users"."id". Booleans are first-class: emitTRUE/FALSE. Unquoted identifiers get folded to lowercase, so quote them to preserve case. - SQLite — accepts both double quotes and backticks for identifiers. No real boolean type: emit
1/0. All strings are TEXT until cast. - SQL Server — identifiers in square brackets:
[users].[id]. Booleans are BIT: emit1/0. Dates needCAST('2026-10-10' AS DATE)if the column is DATETIME.
String Escaping — The Thing That Breaks Everything
SQL strings are delimited by single quotes. A single quote inside a string must be doubled: 'Ada's laptop' becomes'Ada''s laptop'. The tool handles this automatically. Backslashes are a trap: MySQL in default mode treats \' as an escape, but ANSI SQL does not. We emit standard ANSI double-single-quote escaping and it works in every dialect.
Unicode control characters (newlines, tabs) in strings are a different problem. The tool emits them literally inside the quoted string. If your database rejects embedded newlines, use CHAR(10) concatenation on MySQL orE'foo\nbar' escape syntax on PostgreSQL — convert the strings before pasting if you hit this.
Handling Nested Objects and Arrays
Relational databases don't natively store nested JSON (except where a column is explicitly typed as JSON). The tool flattens nested fields in one of two ways:
- Serialize as JSON string (default). A nested object or array becomes a single quoted JSON string. The target column should be TEXT, JSON, or JSONB.
- Flatten with dot-paths. Nested keys become
parent_childcolumn names. Only works for predictable nesting — one level deep is safe, two levels gets messy.
// Input
{ "id": 1, "profile": { "bio": "...", "tags": ["ai", "ml"] } }
// Serialized as JSON
INSERT INTO users (id, profile) VALUES (1, '{"bio":"...","tags":["ai","ml"]}');
// Flattened
INSERT INTO users (id, profile_bio, profile_tags) VALUES (1, '...', '["ai","ml"]');Bulk INSERT Performance
Row-at-a-time inserts take one round trip, one parse, and one plan per row. Bulk inserts take one round trip, one parse, and one plan for the whole batch. On MySQL and PostgreSQL, a bulk insert of 1,000 rows is roughly 50-100x faster than 1,000 single inserts over a network. Three guardrails:
- Batch size. Split arrays over ~1,000 rows into chunks. MySQL
max_allowed_packetdefaults to 64MB; a wide table with blobs will hit this fast. - Transactions. Wrap the bulk INSERTs in
BEGIN; ... COMMIT;so a failure rolls back the whole batch and you don't end up with partially inserted data. - Conflict handling. Add
ON CONFLICT DO NOTHING(PostgreSQL) orINSERT IGNORE(MySQL) to skip duplicates by primary key when re-running imports.
Common Pitfalls
- Inconsistent keys. If row 1 has
{id, name}and row 2 has{id, email}, the generated SQL will have mismatched VALUES. The tool uses the UNION of keys as the column list and fills missing fields with NULL — but your target table needs those columns to be nullable or defaults must exist. - Reserved words as column names.
order,group,userare reserved. Quoting them with the dialect-appropriate style (backticks / double-quotes / brackets) is mandatory. The tool quotes every identifier for safety. - Dates stored as strings."2026-10-10" is a string in JSON. Target DATE columns accept the string in standard dialects, but target DATETIME on SQL Server may need an explicit
CAST. - Case folding on PostgreSQL.
INSERT INTO Users ...without quotes becomesusers. If your table is"Users"(quoted during CREATE), use quoted identifiers everywhere.
Related JSON Tools
- JSON Formatter — pretty-print and validate before converting.
- JSON to CSV — alternative tabular export, useful for bulk import via
COPYorLOAD DATA INFILE. - JSON Flattener — flatten nested objects before conversion if you want flat columns instead of a JSON string.
- JSON Schema Validator — check array consistency before conversion to catch missing fields.