SQL 转 JSON:NULL、布尔值与 0/1 的语义差异
SQL 的 NULL 不是字符串,0/1 也不天然等于布尔值。PostgreSQL 有原生 boolean;MySQL 项目中常用 TINYINT(1) 表示开关,但列定义和驱动返回类型仍需确认。本文只讨论 SQL 行转换为 JSON 时的空值和布尔语义,不讨论 INSERT 解析或表结构自动推断。
一、问题概述:四种看起来相似的值
SQL NULL 表示缺失/未知,JSON null 是一种明确的 JSON 值,字符串 "null" 是文本,数字 0 是数值。true/false 也不能从任意数据库中的 0/1 自动推导。转换前必须知道列的声明类型和业务含义,而不是只观察一次驱动返回值。
二、最小复现:原生 boolean 与 TINYINT(1) 不是同一声明
PostgreSQL 查询可以直接构造 JSON boolean;MySQL 的 TINYINT(1) 是常见约定,具体返回和 JSON 函数行为要以列定义、服务器版本和驱动为准。
-- PostgreSQL
SELECT json_build_object('enabled', enabled, 'note', note) FROM settings;
-- MySQL
SELECT JSON_OBJECT('enabled', enabled, 'note', note) FROM settings;
-- These are different from the string values '0', '1', and 'null'.三、根因:NULL 传播和类型转换由数据库与驱动共同决定
SQL 表达式中的 NULL 会参与三值逻辑,比较结果可能是 UNKNOWN;JSON 序列化通常把列的 NULL 表示为 JSON null,但自定义 COALESCE、IFNULL 或驱动转换可能改变它。PostgreSQL 的 boolean 与 MySQL 的数值开关也可能以不同 JavaScript 类型到达应用层。
四、推荐方案:以列契约为准做显式映射
在转换器中为字段记录 dialect、数据库声明类型和允许的输入集合。原生 boolean 只接受 true/false;约定为开关的 TINYINT(1) 才在明确规则下把 0/1 映射为布尔;未知字符串和其他数字应保留或拒绝,而不是静默转换。NULL 统一映射为 JSON null,除非接口契约规定省略字段。
五、完整代码:显式区分两种布尔来源
以下 TypeScript 不猜测驱动行为。调用方必须提供列语义;字符串 "0" 不会被当成数字 0,未知值会抛出错误,避免数据被悄悄改写。
type BooleanSource = "postgres-boolean" | "mysql-tinyint1";
function toJsonBoolean(value: unknown, source: BooleanSource): boolean | null {
if (value === null) return null;
if (source === "postgres-boolean" && typeof value === "boolean") return value;
if (source === "mysql-tinyint1" && (value === 0 || value === 1)) return value === 1;
throw new TypeError("Value does not match the declared boolean source");
}
console.log(toJsonBoolean(true, "postgres-boolean")); // true
console.log(toJsonBoolean(0, "mysql-tinyint1")); // false
console.log(toJsonBoolean(null, "mysql-tinyint1")); // null
// toJsonBoolean("0", "mysql-tinyint1") throws instead of guessing.六、常见错误方案
用 value || false 会把 NULL、空字符串和其他假值全部压成 false;用 Boolean(value) 会让字符串 "false" 变成 true;用 COALESCE(enabled, false) 会丢失 NULL 的业务含义。把每个 0/1 列都转成 boolean 也会误伤计数、状态码和普通整数。
七、边界条件:NULL、字符串和驱动返回类型
区分字段缺失、SQL NULL、JSON null、空字符串和字符串 "null"。某些驱动可能把数值以字符串返回,不能只凭字符串内容转换;应读取列元数据或使用明确查询别名。若接口需要省略 null 字段,应在序列化层显式实现,而不是依赖 JSON.stringify 的偶然行为。
八、如何验证转换结果
在 MySQL 和 PostgreSQL 分别准备 true/false、0/1、NULL、字符串 "0"、字符串 "null" 和普通整数列。检查数据库 JSON 函数输出和驱动行输出,再断言转换器的类型和值。额外验证 null 是 JSON null 还是字段省略,确保与 API 契约一致。
九、FAQ
问:MySQL TINYINT(1) 就是 boolean 吗?答:它常被用作约定,但仍要依据列定义、项目规范和驱动行为确认。
问:NULL 转 JSON 应该变成 false 吗?答:通常应保留为 JSON null,除非业务明确规定缺省值。
问:为什么 Boolean("false") 是 true?答:JavaScript 按非空字符串判断真值,不会解析字符串内容。
十、总结
SQL 到 JSON 的关键是保留语义:NULL、JSON null、字符串 null、0/1 和 boolean 不是同一种值。PostgreSQL 原生 boolean 与 MySQL TINYINT(1) 要分开处理,转换器应依赖列契约、显式映射并拒绝未知值,再用两种数据库的边界样本验证结果。
来源与延伸阅读
技术审校所依据的规范与权威参考资料。
- PostgreSQL Documentation — SQL Syntax
PostgreSQL Global Development Group
- RFC 8259 — The JavaScript Object Notation (JSON) Data Interchange Format
RFC Editor
相关文章
SQL Dump 转 JSON Mock:解析、脱敏、批量与类型边界
从 SQL dump 生成测试 JSON 时,分别处理 INSERT parser、敏感字段脱敏、批量行和 SQL 类型限制,不把 Mock 转换当成数据库迁移。
错误排查SQL 转 JSON:为什么正则难以可靠解析 INSERT
用最小示例说明正则提取 INSERT 会被引号、转义、批量 VALUES、注释和 SQL 方言打破,并提供仅用于分句的保守扫描器与 parser 选型边界。
错误排查SQL Formatter 方言兼容:关键字、函数与语法保真
说明 SQL formatter 为什么必须声明方言,比较 PostgreSQL、MySQL 与 SQLite 的关键字、函数、引号和语法保真边界。
继续阅读
可打开关联的浏览器工具,使用自己的样本验证文中的处理流程。
打开关联工具