mysql 5.7+ 支持直接用 json_object() 或 json_array() 在 insert 中构造合法 json 值,避免字符串拼接引发的 sql 注入与非法 json 错误;json_object 会跳过 null 键值,不支持默认空对象,且需注意路径嵌套限制。

INSERT 时直接用 JSON_OBJECT 或 JSON_ARRAY 构造值
MySQL 5.7+ 和 PostgreSQL 12+ 都支持在 INSERT 语句中直接构造 JSON,避免先拼字符串再解析。MySQL 用 JSON_OBJECT(key, value) 和 JSON_ARRAY(),PostgreSQL 用 json_build_object() 或字面量写法 '{"a": 1}'::json。
常见错误是把 JSON 当成字符串硬拼:INSERT INTO t (data) VALUES ('{"id":' || id_var || '}')——这既不安全(SQL 注入风险),也不合法(MySQL 会报 Invalid JSON text)。
- MySQL 正确写法:
INSERT INTO users (profile) VALUES (JSON_OBJECT('name', 'Alice', 'age', 30, 'tags', JSON_ARRAY('dev', 'sql'))) - PostgreSQL 正确写法:
INSERT INTO users (profile) VALUES (json_build_object('name', 'Alice', 'age', 30, 'tags', ARRAY['dev','sql'])) - 若字段含 NULL 值,MySQL 的
JSON_OBJECT会跳过该键;PostgreSQL 的json_build_object会保留"key": null,需提前COALESCE过滤
UPDATE 时用 JSON_SET / jsonb_set 避免全量重写
频繁更新 JSON 字段某几个字段时,全量覆盖(UPDATE ... SET data = '{"a":1,"b":2}')会导致 MVCC 膨胀、索引失效、锁粒度变大。应优先用原生函数局部更新。
MySQL 5.7+ 提供 JSON_SET()(插入或替换)、JSON_INSERT()(仅插入)、JSON_REPLACE()(仅替换);PostgreSQL 用 jsonb_set()(要求目标为 jsonb 类型)。
- MySQL 示例:
UPDATE logs SET payload = JSON_SET(payload, '$.status', 'done', '$.at', NOW()) WHERE id = 123 - PostgreSQL 示例:
UPDATE logs SET payload = jsonb_set(payload, '{status}', '"done"', true) WHERE id = 123(第四个参数true表示缺失路径时自动创建) - 注意:MySQL 的
JSON_SET对不存在的路径会创建,但无法嵌套创建多层(如'$.a.b.c'要求a和b已存在),否则静默失败
批量插入时慎用 JSON_CONTAINS 或 GIN 索引触发的隐式开销
如果表上有基于 JSON 字段的生成列 + 索引(如 MySQL 的 GENERATED COLUMN + INDEX,或 PostgreSQL 的 jsonb_path_ops GIN 索引),大批量 INSERT 会显著拖慢速度——因为每行都要解析 JSON 并更新索引项。
典型场景:日志表带 payload->>'$.user_id' 生成列并建了索引,但导入百万条原始日志时发现插入变慢 5 倍。
- 临时方案:导入前禁用索引(MySQL 不支持禁用生成列索引,可
DROP INDEX后重建;PostgreSQL 可SET enable_indexscan = off配合CREATE INDEX CONCURRENTLY) - 更稳做法:先插入裸 JSON,再用单条
UPDATE批量补全生成列,最后建索引 - PostgreSQL 中,
jsonb_path_ops索引比默认jsonb_ops更省空间但不支持@?等高级操作符,选型要匹配查询模式
NULL 值和空对象的语义差异必须显式处理
NULL、'null' 字符串、空对象 {}、空数组 [] 在 JSON 处理中行为完全不同,但容易被忽略。
例如 MySQL 中 JSON_EXTRACT(col, '$.field') 对缺失字段返回 NULL,但 col->>'$.field' 返回 NULL(字符串上下文)或空字符串(取决于 SQL 模式);PostgreSQL 中 payload->>'field' 对缺失字段返回 NULL,而 payload->'field' 返回 NULL::jsonb。
- 判断字段是否存在:MySQL 用
JSON_CONTAINS_PATH(col, 'one', '$.field'),PostgreSQL 用payload ? 'field' - 插入空值时,明确写
JSON_OBJECT()或'{}'::jsonb,而不是NULL——除非业务真需要区分“无数据”和“数据为空” - 应用层序列化时,确认 SDK 是否将
null字段省略(影响JSON_CONTAINS_PATH判断)还是保留为"key": null
最易被绕过的点:跨数据库迁移 JSON 数据时,MySQL 的 JSON 类型不校验重复 key(后出现的覆盖前一个),而 PostgreSQL 的 jsonb 会自动去重合并——如果源数据本身含重复 key,结果可能不一致。











