How to convert json to mysql insert statements
- Step 1Get a JSON array of flat objects — The converter needs a top-level array like
[{"id":1,...},{"id":2,...}](a single object also works as a one-row table). If your dump nests rows under a key ({"rows":[...]}), pull the array out with json-path-extractor using$.rows[*]first. - Step 2Drop the file on the converter — Drag a
.jsonfile onto the dropzone..xlsx/.xls/.odsare accepted too (first sheet → JSON array). A raw.csvis not parsed as rows — convert it with csv-to-json first. - Step 3Name the table and select MySQL — Enter the destination in Table name (default
data) and click the MySQL dialect button. Identifiers switch to backticks, booleans toTINYINT(1)with1/0, and decimals toREAL. - Step 4Drop unwanted columns — List internal keys in Exclude columns, comma-separated, e.g.
internal_id, _meta. They are removed from theCREATE TABLEand from everyINSERTrow so the imported table is exactly the shape you want. - Step 5Set CREATE TABLE and INSERT IGNORE — Keep Include CREATE TABLE on to define the table, or off for INSERT-only against an existing one. IF NOT EXISTS protects re-runs. Turn on ON CONFLICT DO NOTHING to get
INSERT IGNORE INTO, which silently skips duplicate-key rows on MySQL. - Step 6Generate and import — Click Generate SQL, then Copy or Download SQL. Review the inferred types — money columns show as
REAL; you'll likely wantDECIMAL(10,2)instead. Import withmysql -u user -p mydb < output.sqlor paste into a client. The stats line shows rows, columns, and statement count.
Real MySQL options and what they emit
The converter's controls mapped to the exact MySQL output. Defaults are the values the tool ships with.
| Option | Effect in MySQL output | Default |
|---|---|---|
| Table name | Backtick-quoted target, e.g. `CREATE TABLE IF NOT EXISTS users | data |
| Dialect = MySQL | Backtick identifiers; booleans as TINYINT(1) with 1/0; decimals typed REAL | PostgreSQL (you must switch) |
| Exclude columns | Comma-separated keys dropped from CREATE TABLE and every INSERT row | empty |
| Include CREATE TABLE | Prepends the CREATE TABLE block; off = INSERTs only | On |
| IF NOT EXISTS | Adds IF NOT EXISTS so re-runs don't error on an existing table | On |
| ON CONFLICT DO NOTHING | Switches the keyword to INSERT IGNORE INTO (NOT ON CONFLICT syntax) | Off |
How JSON value types map to MySQL column types
Type is inferred per key from all non-null values in the array. One off-type value loosens the column; an all-null or absent key becomes TEXT.
| JSON value(s) for a key | Inferred MySQL type | Example value in INSERT |
|---|---|---|
All true/false | TINYINT(1) | 1 / 0 |
| All whole numbers | INTEGER | 42 |
| Any decimal | REAL | 9.5 |
| Strings, dates, UUIDs | TEXT | '2026-01-01' |
Nested object {...} | TEXT (JSON-stringified) | '{"plan":"pro"}' |
Array [...] | TEXT (JSON-stringified) | '[1,2,3]' |
null everywhere / key absent | TEXT; value NULL | NULL |
Free vs Pro limits
JSON to SQL is a Pro tool. Limits come from the json family in the tier table; the free preview is gated by a code-generation quota.
| Limit | Free | Pro |
|---|---|---|
| Max input file size | 2 MB | 100 MB |
| Files per run | 1 | 10 |
| Rows per INSERT batch | 100 (fixed) | 100 (fixed) |
Cookbook
JSON in, MySQL out — copied straight from the converter so the backtick quoting, TINYINT(1) booleans, and INSERT IGNORE behaviour are exactly what you'll get.
Bulk seed with TINYINT(1) booleans and backticks
ExampleMySQL has no native boolean, so the active flag becomes a TINYINT(1) column with 1/0. Identifiers are backtick-quoted; the apostrophe is doubled.
Input:
[
{"id": 1, "name": "O'Hara", "active": true},
{"id": 2, "name": "Lee", "active": false}
]
Output (MySQL):
CREATE TABLE IF NOT EXISTS `members` (
`id` INTEGER,
`name` TEXT,
`active` TINYINT(1)
);
INSERT INTO `members` (`id`, `name`, `active`)
VALUES
(1, 'O''Hara', 1),
(2, 'Lee', 0);Safe re-seed with INSERT IGNORE
ExampleEnabling the conflict toggle in MySQL mode does not emit ON CONFLICT — it switches the keyword to INSERT IGNORE INTO, MySQL's duplicate-key skip.
Config: ON CONFLICT DO NOTHING enabled Output (MySQL): INSERT IGNORE INTO `roles` (`id`, `name`) VALUES (1, 'admin'), (2, 'editor'); -- Rows whose PK/unique key already exist are skipped -- silently. This is NOT ON DUPLICATE KEY UPDATE; there -- is no upsert option.
Nested array stored as a TEXT column
ExampleA tags array isn't expanded into rows or a JSON column — it is JSON-stringified into one TEXT value. Store it, or flatten the source first.
Input:
[{"id": 1, "tags": ["alpha", "beta"]}]
Output (MySQL):
CREATE TABLE IF NOT EXISTS `posts` (
`id` INTEGER,
`tags` TEXT
);
INSERT INTO `posts` (`id`, `tags`)
VALUES
(1, '["alpha","beta"]');
-- Change `tags` to JSON type in your real schema if you
-- want JSON_EXTRACT()/-> queries to work.INSERT-only against an existing table
ExampleTurn off Include CREATE TABLE when the table already exists with your own AUTO_INCREMENT and constraints. You get only the inserts.
Config: Include CREATE TABLE = off, dialect = MySQL Output (MySQL): INSERT INTO `users` (`id`, `email`) VALUES (1, 'a@x.com'), (2, 'b@x.com'); -- No CREATE TABLE emitted, so your existing column types, -- PRIMARY KEY, and AUTO_INCREMENT are untouched.
Decimal column lands as REAL — adjust for money
ExampleA price with decimals is typed REAL by inference. For currency you want DECIMAL(10,2); edit the CREATE TABLE before importing.
Input:
[{"id": 1, "price": 19.99}, {"id": 2, "price": 5.00}]
Output (MySQL):
CREATE TABLE IF NOT EXISTS `products` (
`id` INTEGER,
`price` REAL
);
INSERT INTO `products` (`id`, `price`)
VALUES
(1, 19.99),
(2, 5);
-- Note 5.00 -> 5 (JSON number). For exact money, change
-- `price` REAL to `price` DECIMAL(10,2) by hand.Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Conflict toggle produces INSERT IGNORE, not ON CONFLICT
By designIn MySQL mode the ON CONFLICT DO NOTHING toggle switches the keyword to INSERT IGNORE INTO. It never emits ON CONFLICT syntax (which would error in MySQL). INSERT IGNORE skips rows that violate a unique or primary key — but only if such a constraint exists on the target table. It is not an upsert.
No ON DUPLICATE KEY UPDATE / upsert
Not supportedThere is no option to generate ON DUPLICATE KEY UPDATE. The only conflict behaviour is the skip-on-duplicate INSERT IGNORE. If you need MySQL upserts, generate the inserts here and rewrite them into INSERT ... ON DUPLICATE KEY UPDATE col=VALUES(col) manually.
Boolean stored as TINYINT(1), value 1/0
ExpectedMySQL has no real BOOLEAN type, so the column is TINYINT(1) and values are 1/0, not TRUE/FALSE. This is the correct MySQL idiom (MySQL aliases BOOLEAN to TINYINT(1) anyway). If you switch the dialect back to PostgreSQL or SQLite you'll see BOOLEAN with TRUE/FALSE instead.
Money column inferred as REAL
ExpectedDecimals make a column REAL, a binary float that rounds. For currency, edit the generated type to DECIMAL(p,s) after generating, or store money as integer cents in the JSON so the column infers as INTEGER.
Decimal like 5.00 collapses to 5
ExpectedJSON numbers don't preserve trailing zeros, so 5.00 becomes the number 5 and is emitted as 5. The inferred column type still depends on whether any value in the column was a decimal. If precision/formatting matters, store the value as a string in the source JSON (it will then be a TEXT column, quoted).
Nested object/array becomes TEXT
By designObjects and arrays are JSON.stringify-ed into a TEXT value, not a MySQL JSON column or child table. To use JSON_EXTRACT/-> you must change the column type to JSON in your schema, or flatten the JSON first with json-flattener.
Raw CSV dropped instead of JSON
Invalid inputOnly .json and spreadsheet files (.xlsx/.xls/.ods) become rows. A .csv is passed straight to JSON.parse and fails. Run it through csv-to-json first, then convert the resulting array here.
No ENGINE, charset, or AUTO_INCREMENT in CREATE TABLE
By designThe generated CREATE TABLE lists columns and inferred types only — no ENGINE=InnoDB, no DEFAULT CHARSET, no PRIMARY KEY, no AUTO_INCREMENT. Add those in your real DDL, or generate INSERT-only (turn off Include CREATE TABLE) against a table you already defined.
Input exceeds the size limit
Upgrade requiredFree tier caps input at 2 MB, Pro at 100 MB. Larger files are rejected before conversion. Split the array into smaller files and import them in sequence, optionally with INSERT IGNORE so re-runs are safe.
Frequently asked questions
Why are booleans showing as 1 and 0 instead of TRUE/FALSE?
Because you're in MySQL mode. MySQL has no native boolean type, so the converter emits a TINYINT(1) column with 1/0 values — which is exactly how MySQL stores booleans (BOOLEAN is just an alias for TINYINT(1)). PostgreSQL and SQLite modes emit BOOLEAN columns with TRUE/FALSE instead.
Does the conflict option emit ON CONFLICT DO NOTHING in MySQL?
No — that's Postgres/SQLite syntax and would error in MySQL. In MySQL mode the same toggle switches the statement to INSERT IGNORE INTO, which silently skips rows that would violate a unique or primary key. It only has an effect if such a constraint exists on the target table.
Can I generate an upsert (ON DUPLICATE KEY UPDATE)?
No. The only conflict behaviour is the skip-on-duplicate INSERT IGNORE. There is no upsert option. Generate the inserts, then hand-edit them into INSERT ... ON DUPLICATE KEY UPDATE if you need merge semantics.
Why is my price column REAL instead of DECIMAL?
Inference only separates integers from other numbers. Any decimal makes the column REAL, a binary float that can round. For money, change the generated type to DECIMAL(10,2), or store amounts as integer cents in the JSON so the column infers as INTEGER.
Does it add a PRIMARY KEY or AUTO_INCREMENT?
No. The CREATE TABLE has column names and types only — no keys, no AUTO_INCREMENT, no ENGINE or charset. Either add those to the DDL manually, or create the table yourself with the right definition and run the converter with Include CREATE TABLE turned off so you only get INSERTs.
How does it escape apostrophes and quotes?
Single quotes are doubled ('O''Hara'), the SQL-standard escape that MySQL accepts. There's no backslash escaping. It applies to every string value and to JSON-stringified nested objects/arrays, so apostrophes in names and free text never break the statement.
How many rows go into each INSERT statement?
100 rows per multi-row INSERT ... VALUES — a fixed batch size with no UI control. A 500-row array yields five INSERT statements (plus CREATE TABLE). The stats line reports the total statement count after generating.
What happens to nested JSON in MySQL output?
Nested objects and arrays are JSON-stringified into a TEXT column, e.g. '["a","b"]'. They are not stored as a MySQL JSON column. If you want JSON_EXTRACT/-> to work, change the column type to JSON in your schema, or flatten with json-flattener before converting.
Will it work for MariaDB too?
Yes. The MySQL dialect output (backticks, TINYINT(1), INSERT IGNORE) is fully compatible with MariaDB. There's no separate MariaDB dialect because the syntax this tool generates is identical across both.
Can I import a JSON file with rows wrapped under a key?
Not directly — the top-level value must be the array (or a single object). For {"data":[...]}, extract the inner array first with json-path-extractor ($.data[*]), then convert the array here.
Is the data sent to a server?
No. Conversion is entirely in-browser. The JSON and the generated SQL — including any production values — stay on your machine. Only an anonymous run counter (no content) is logged for dashboard stats.
Can I generate INSERTs for multiple tables at once?
No. Each run targets one table named in the Table name box. For a multi-table import, split your JSON by entity, run the converter once per table, and concatenate the resulting files in foreign-key dependency order before importing.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.