
停止手动编写INSERT语句:使用JSON转SQL工具
📷 Christina Morillo / Pexels停止手动编写INSERT语句:使用JSON转SQL工具
将200行的JSON导出手动转换为INSERT语句既痛苦又容易出错。以下是如何自动化以及需要注意的事项。
你从第三方服务导出了200行用户数据。格式是JSON。你的数据库需要SQL INSERT语句。不知为何,你在考虑手动编写它们。
这个思路大概是这样的:打开JSON文件,查看结构,写出CREATE TABLE语句,然后开始编写INSERT语句。第一个花了两分钟。第二个花了一分钟。到第十个你已经有了一套流程。到第五十个时,你引入了一个拼写错误,在导入因约束错误失败之前你都不会注意到。
有更好的方法。JSON转SQL工具获取你的JSON数组,推断架构,并生成可直接运行的SQL — 带有正确列名和引号值的批量INSERT语句。本指南介绍它的工作原理、如何有效使用它,以及哪些地方你仍需要手动清理。
工具实际上做什么
简而言之:你粘贴一个JSON对象数组,选择SQL方言,工具生成两样东西 — 基于推断架构的CREATE TABLE语句,以及数据的一系列INSERT语句。
类型推断是有趣的部分。工具查看你的JSON中的值,并对SQL类型做出合理的猜测。字符串变为VARCHAR或TEXT。数字变为INT或FLOAT。布尔值根据你的方言变为BOOLEAN或TINYINT(1)。Null值默认为可为null的VARCHAR。
它还自动处理SQL方言之间的格式差异 — MySQL的反引号引用列名,PostgreSQL的双引号标识符,SQL Server文本列的NVARCHAR。
一个真实的例子
假设你有来自SaaS应用的JSON导出。用户表导出看起来像这样:
[
{
"id": 1,
"name": "Alice Chen",
"email": "alice@example.com",
"age": 31,
"active": true,
"created_at": "2025-01-15T09:23:00Z"
},
{
"id": 2,
"name": "Ben Kowalski",
"email": "ben@example.com",
"age": 28,
"active": false,
"created_at": "2025-02-03T14:55:00Z"
},
{
"id": 3,
"name": "Sara Okonkwo",
"email": "sara@example.com",
"age": 34,
"active": true,
"created_at": "2025-03-11T08:00:00Z"
}
]
粘贴进去,选择MySQL,输出看起来像:
CREATE TABLE `users` (
`id` INT,
`name` VARCHAR(255),
`email` VARCHAR(255),
`age` INT,
`active` BOOLEAN,
`created_at` VARCHAR(255)
);
INSERT INTO `users` (`id`, `name`, `email`, `age`, `active`, `created_at`) VALUES
(1, 'Alice Chen', 'alice@example.com', 31, TRUE, '2025-01-15T09:23:00Z'),
(2, 'Ben Kowalski', 'ben@example.com', 28, FALSE, '2025-02-03T14:55:00Z'),
(3, 'Sara Okonkwo', 'sara@example.com', 34, TRUE, '2025-03-11T08:00:00Z');
对于200行,你会得到相同的结构,但有一批200个值。这大约是粘贴和点击三十秒的工作,而不是十五分钟容易出错的打字。
注意输出中的几点。created_at字段被类型化为VARCHAR(255)而不是DATETIME — 我们稍后会讲到为什么这很重要。表名默认为users;工具可能让你指定它。CREATE TABLE不包含PRIMARY KEY或NOT NULL约束 — 这些是你的工作。
值得了解的方言差异
"SQL方言"选择器比看起来更重要。数据库之间的语法差异很小,但如果选错了就会出错。
MySQL使用反引号引用标识符:`column_name`。如果你的列名恰好与MySQL保留字冲突(如order、key、index或values),这一点很重要。MySQL在旧架构中也使用TINYINT(1)表示布尔值,尽管MySQL 5.7+中BOOLEAN可以工作。
PostgreSQL使用双引号引用标识符:"column_name"。它具有原生的BOOLEAN和TIMESTAMP WITH TIME ZONE类型。它还有用于自动递增主键的SERIAL和BIGSERIAL,这与MySQL的AUTO_INCREMENT不同。
SQLite对类型很宽容 — SQLite使用"类型亲和性"而不是严格类型,因此声明为TEXT的列可以存储整数,反之亦然。对于SQLite输出,你通常会看到更简单的类型名称,如TEXT和INTEGER。
SQL Server对Unicode文本字符串使用NVARCHAR代替VARCHAR,对标识符使用方括号引用:[column_name]。SQL Server还使用BIT表示布尔值,而不是BOOLEAN或TINYINT。
如果你要导入到特定数据库,请选择该方言。如果你将手动运行SQL并进行调整,任何方言都能给你一个可用的起点 — 只需注意你需要更改的内容。
类型推断:哪些情况正确,哪些情况失败
对于常见情况,工具相当不错:
- JSON中的
123变为SQL中的INT 123.45变为FLOAT或DECIMAL"hello"变为VARCHAR(255)或TEXTtrue/false变为BOOLEAN或TINYINT(1)null变为可为null的列,通常类型为VARCHAR
它失败的地方是JSON使用字符串表示非字符串数据的任何地方。
电话号码是典型的陷阱。如果你的JSON有"phone": "12345",这是JSON中的字符串,你会得到VARCHAR(255) — 这是正确的,因为电话号码应该存储为字符串(前导零、带+的国家代码、格式化的号码)。但如果有人在源系统中将电话号码存储为裸整数,你可能会看到"phone": 12345并得到INT。然后你的导入会静默地丢弃前导零和格式。
日期和时间字段几乎总是作为VARCHAR出现,因为JSON没有原生日期类型。"2025-01-15T09:23:00Z"就JSON而言只是一个字符串。如果你需要适当的DATETIME或TIMESTAMP列,请在生成后更改CREATE TABLE语句中的列类型。数据本身在插入到TIMESTAMP列时SQL会解析ISO 8601字符串,因此仍然可以正确导入。
看起来像数字的ID更棘手。值为1042的id字段将被类型化为INT。这可能没问题 — 或者你的系统可能使用UUID或字母数字ID,在你的样本的前几行中恰好全是数字。如果第201行有id: "a1b2c3",你的导入会失败。
大型文本字段将被类型化为VARCHAR(255)。如果字段包含较长的内容 — 文章正文、描述、序列化数据 — 你需要在MySQL中将类型更改为TEXT或LONGTEXT,或在PostgreSQL中更改为TEXT(没有长度限制)。
经验法则:在运行之前始终检查生成的CREATE TABLE语句。INSERT语句通常没问题,但架构推断是需要人工判断的地方。
批量插入:为什么重要
对于小型数据集 — 十到二十行 — 是否一次一行插入或批量插入并不重要。对于较大的数据集,差异是显著的。
单行INSERT看起来像这样:
INSERT INTO `users` (`id`, `name`, `email`) VALUES (1, 'Alice', 'alice@example.com');
INSERT INTO `users` (`id`, `name`, `email`) VALUES (2, 'Ben', 'ben@example.com');
-- ... 还有198个语句
批量INSERT看起来像这样:
INSERT INTO `users` (`id`, `name`, `email`) VALUES
(1, 'Alice', 'alice@example.com'),
(2, 'Ben', 'ben@example.com'),
(3, 'Sara', 'sara@example.com'),
-- ... 最多100行
;
INSERT INTO `users` (`id`, `name`, `email`) VALUES
(101, 'David', 'david@example.com'),
-- ... 下一批
;
批量版本更快有两个原因。首先,每批只有一个网络往返,而不是每行一个。其次,数据库可以更好地优化单个大写入,而不是许多小写入 — 事务开销、索引更新和预写日志每个语句发生一次,而不是每行一次。
工具通常将批次限制在100-500行,而不是将所有内容放在一个语句中,原因是内存和可靠性。一个10,000行的单个INSERT语句可能会触及内存限制,解析慢,如果它在中途失败,你对哪些行是问题所在没有有用的反馈。较小的批次更实用。
工具不会做什么
清楚地了解限制可以节省你以后的调试时间。
没有外键。 工具对表之间的关系一无所知。如果你的JSON有引用另一个表的user_id字段,你会得到一个普通的INT列 — 没有FOREIGN KEY约束,没有REFERENCES子句。你需要手动添加这些。
没有索引。 生成的CREATE TABLE没有索引定义。如果你要导入大型数据集然后查询它,你需要在你将要过滤或连接的列上添加INDEX或UNIQUE INDEX定义。这始终是导入后的步骤。
没有主键标记。 工具可能创建一个id INT列,但不会添加PRIMARY KEY或AUTO_INCREMENT。如果你想让id成为一个适当的自动递增主键,在MySQL中向该列定义添加PRIMARY KEY AUTO_INCREMENT,或在PostgreSQL中切换到SERIAL。
没有复杂或嵌套类型。 如果字段值是对象("address": {"street": "123 Main St", "city": "Portland"})或数组("tags": ["developer", "remote"]),工具无法将其展平为关系架构。它通常要么将其序列化为JSON字符串,要么跳过该字段。你需要决定:将其存储为JSON列(MySQL 5.7+和PostgreSQL支持原生JSON列),展平为单独的列,还是规范化为单独的表。
没有upsert逻辑。 生成的语句是没有冲突处理的普通INSERT。如果你对已有数据且主键重叠的表运行它们,你会得到约束违反错误。对于幂等导入,你需要MySQL中的INSERT IGNORE、PostgreSQL中的ON CONFLICT DO NOTHING或SQL Server中的MERGE — 这些工具都不会自动添加。
没有事务。 输出没有用BEGIN TRANSACTION / COMMIT包裹。对于大型导入,将整个过程包裹在事务中是个好习惯 — 如果中途失败,你可以干净地回滚。在生成的语句周围自己添加它。
何时使用此工具与其他选项
JSON转SQL工具是特定情况的正确工具。它不是所有情况的正确工具。
使用它当:
- 你有来自外部源的一次性数据导入
- 你在数据库之间迁移数据,需要快速SQL转储
- 你正在用看起来真实的数据设置演示或测试数据库
- 你直接使用SQL而没有运行应用程序栈
- 你需要与只有SQL访问权限的人共享数据
改用你的ORM或ETL工具当:
- 你需要可重复的、版本控制的种子数据 — 像Prisma的
db seed、Django的fixtures或Rails的种子文件这样的工具与你的迁移工作流集成,可以安全地重新运行 - 导入很复杂,不仅仅是插入数据,还涉及转换数据
- 你需要以编程方式处理关系和外键约束
- 你在按计划定期执行此操作 — 编写适当的ETL脚本或使用Apache NiFi、dbt甚至带pandas和sqlalchemy的简单Python脚本等工具
JSON转SQL工具本质上是"我只需要将这些数据放入表中"场景的捷径。当场景比这更复杂时,你需要更复杂的工具。
实际工作流程
以下是效果很好的顺序:
-
首先格式化你的JSON。 如果你的导出是压缩的,先通过JSON格式化器粘贴它。这让你可以在转换之前查看结构并发现任何问题。
-
检查数组结构。 工具期望一个JSON对象数组,其中每个对象都有相同的顶级键。如果你的导出包裹在根对象(
{"data": [...]})中而不是裸数组,先提取数组。 -
选择你的方言并粘贴JSON。复制生成的SQL。
-
检查CREATE TABLE。 在运行任何内容之前,仔细阅读列类型,修复任何看起来不对的地方 — 特别是日期字段、大型文本字段以及任何实际上应该是字符串的数字字段。
-
添加你需要的约束。 标记主键,在适当的地方添加
NOT NULL,在电子邮件列上添加UNIQUE,并添加任何FOREIGN KEY约束。 -
先运行CREATE TABLE,然后运行INSERT。如果表已经存在并且你在重新导入,决定是先
DROP TABLE IF EXISTS还是使用某种冲突解决策略。 -
验证行数。 导入后,运行
SELECT COUNT(*) FROM your_table;并与你JSON中的行数比较。如果匹配,你就完成了。
格式化输出SQL
生成的SQL通常可读,但并不总是符合你的标准。如果你想要格式一致的INSERT语句 — 对齐的列、关键字大小写一致 — 将输出通过SQL格式化器运行。这不会改变语义,但使SQL更容易审查和比较差异。
总结
为大型JSON数据集手动编写INSERT语句是自动化最有意义的任务之一。JSON转SQL工具处理机械转换:读取你的数组,推断类型,生成架构,并以你的目标方言生成批量INSERT语句。
仍然需要你注意的部分是架构审查(类型、约束、索引)、处理嵌套数据,以及如果你需要重新运行则使INSERT逻辑具有幂等性。工具处理纯机械的90%。剩余的10%是任何自动化都无法替代的数据库设计判断。
如果你经常处理JSON数据,还有两个其他工具值得与此工具一起收藏:用于审查和清理输入的JSON格式化器,以及在将生成的输出提交到迁移文件之前整理它的SQL格式化器。