五大主流数据库完整数据类型对照速查表,覆盖整数、浮点定点、字符串、日期时间、二进制、布尔、 JSON、枚举、UUID 与网络地址等 11 大类共 60+ 种类型, 每行标注取值范围、占用字节与选型建议。 支持实时搜索过滤与任选两个数据库左右对照高亮, 并附常见跨库迁移坑位与字段选型决策指引。
| 分类 | 用途 / 通用类型 | MySQL | PostgreSQL | Oracle | SQL Server | SQLite | 取值范围 / 占用字节 | 使用建议 |
|---|---|---|---|---|---|---|---|---|
| 整数 | 极小整数(1 字节) | TINYINT | SMALLINT | NUMBER(3) | TINYINT | INTEGER | -128 ~ 127;1 字节。SQL Server 的 TINYINT 是无符号 0 ~ 255 | 存年龄、状态码、小型枚举值。PostgreSQL 没有 1 字节整数,最小就是 SMALLINT |
| 整数 | 无符号极小整数 | TINYINT UNSIGNED | SMALLINT + CHECK | NUMBER(3) + CHECK | TINYINT | INTEGER | 0 ~ 255;1 字节 | 只有 MySQL 与 SQL Server 原生支持,其他库需要用 CHECK 约束模拟无符号 |
| 整数 | 小整数(2 字节) | SMALLINT | SMALLINT / INT2 | NUMBER(5) | SMALLINT | INTEGER | -32,768 ~ 32,767;2 字节 | 存年份、数量、较小的计数器,比 INT 省一半空间 |
| 整数 | 中整数(3 字节) | MEDIUMINT | INTEGER | NUMBER(7) | INT | INTEGER | -8,388,608 ~ 8,388,607;3 字节 | MySQL 独有类型,迁移到其他库统一升为 4 字节 INT |
| 整数 | 标准整数(4 字节) | INT / INTEGER | INTEGER / INT4 | NUMBER(10) | INT | INTEGER | -2,147,483,648 ~ 2,147,483,647;4 字节 | 最常用的整数类型,绝大多数计数、外键、ID 场景的默认选择 |
| 整数 | 大整数(8 字节) | BIGINT | BIGINT / INT8 | NUMBER(19) | BIGINT | INTEGER | 约 -9.22×10¹⁸ ~ 9.22×10¹⁸;8 字节 | 存雪花 ID、Unix 毫秒时间戳、以「分」为单位的金额、超大计数 |
| 整数 | 无符号大整数 | BIGINT UNSIGNED | NUMERIC(20) | NUMBER(20) | DECIMAL(20,0) | INTEGER(有符号) | 0 ~ 18,446,744,073,709,551,615;8 字节 | 迁移高危项:只有 MySQL 有无符号 BIGINT,其他库要降级为定点数,性能与索引都会变差 |
| 整数 | 位串 / 标志位 | BIT(n) | BIT(n) / BIT VARYING | RAW / NUMBER | BINARY / INT | INTEGER | n 位二进制,1 ~ 64 位;按位打包存储 | 存权限位图、多选标志。可读性差,字段少时建议改用多个布尔列 |
| 浮点定点 | 单精度浮点 | FLOAT | REAL / FLOAT4 | BINARY_FLOAT | REAL / FLOAT(24) | REAL | 约 ±3.4×10³⁸,约 7 位有效数字;4 字节 | 只适合science/传感器等可容忍误差的场景,绝对不要存金额 |
| 浮点定点 | 双精度浮点 | DOUBLE | DOUBLE PRECISION / FLOAT8 | BINARY_DOUBLE | FLOAT(53) | REAL | 约 ±1.8×10³⁰⁸,约 15 位有效数字;8 字节 | 科学计算、经纬度、统计指标可用;仍存在二进制舍入误差 |
| 浮点定点 | 精确定点小数(金额首选) | DECIMAL(p,s) | NUMERIC(p,s) | NUMBER(p,s) | DECIMAL(p,s) | NUMERIC(亲和) | MySQL 最大 65 位精度;PG 精度理论无限;每 9 位约 4 字节 | 存金额、汇率、税率一律用 DECIMAL / NUMERIC,运算完全精确,不会出现 0.1+0.2≠0.3 |
| 浮点定点 | 货币金额 | DECIMAL(19,4) | MONEY / NUMERIC(19,4) | NUMBER(19,4) | MONEY / DECIMAL(19,4) | INTEGER(存分) | DECIMAL(19,4) 可表示万亿级金额、精确到 0.0001 | PG 的 MONEY 与 SQL Server 的 MONEY 都依赖区域设置,跨库不安全,统一用 DECIMAL(19,4) 最稳妥 |
| 浮点定点 | 超高精度大数 | DECIMAL(65,30) | NUMERIC(无参) | NUMBER(38,x) | DECIMAL(38,x) | TEXT | MySQL 65 位;Oracle / SQL Server 38 位;PG 最大 131072 位整数部分 | 加密货币、科学计量等超长小数,注意 Oracle 与 SQL Server 的 38 位天花板 |
| 浮点定点 | 百分比 / 比率 | DECIMAL(5,4) | NUMERIC(5,4) | NUMBER(5,4) | DECIMAL(5,4) | NUMERIC | -9.9999 ~ 9.9999,保留 4 位小数 | 存 0.0825 表示 8.25%。建议统一存小数而不是存 8.25 这种「百分数值」,避免歧义 |
| 浮点定点 | 可变精度浮点 | FLOAT(p) | FLOAT(p) | FLOAT(p) | FLOAT(p) | REAL | p ≤ 24 时按单精度存储,p ≥ 25 时按双精度 | 各库对 p 的解释不同(Oracle 按二进制位,MySQL 按十进制位),迁移时务必显式改写 |
| 浮点定点 | 无参数数值 | DECIMAL(默认 10,0) | NUMERIC(任意精度) | NUMBER(浮动精度) | DECIMAL(默认 18,0) | NUMERIC | 各库默认精度差异极大,从 10 位到无限不等 | 迁移重灾区:Oracle 的裸 NUMBER 到 MySQL 会被截成整数,必须显式指定 (p,s) |
| 字符串 | 定长字符串 | CHAR(n) | CHAR(n) / BPCHAR | CHAR(n) | CHAR(n) | TEXT | MySQL 最大 255 字符;Oracle 2000 字节;SQL Server 8000 字节 | 只用于长度真正固定的值(性别、国家码、MD5)。不足位会补空格,是常见的比较陷阱 |
| 字符串 | 变长字符串(最常用) | VARCHAR(n) | VARCHAR(n) | VARCHAR2(n) | VARCHAR(n) / NVARCHAR(n) | TEXT | MySQL 行内最大 65535 字节;Oracle 4000 字节(12c 起可 32767);SQL Server 8000 | Oracle 里请用 VARCHAR2 而不是 VARCHAR,后者语义保留可能变化 |
| 字符串 | 不限长度文本 | TEXT / LONGTEXT | TEXT | CLOB | VARCHAR(MAX) | TEXT | PG TEXT 上限约 1GB;SQL Server MAX 为 2GB;Oracle CLOB 可达 128TB | PostgreSQL 的 TEXT 与 VARCHAR 性能完全一致,PG 里可以放心用 TEXT |
| 字符串 | Unicode 定长 | CHAR(n)(utf8mb4) | CHAR(n) | NCHAR(n) | NCHAR(n) | TEXT | NCHAR 按字符计数,每字符 2 字节(UTF-16) | SQL Server 与 Oracle 需要区分 N 前缀类型,MySQL/PG/SQLite 默认就是 Unicode |
| 字符串 | Unicode 变长 | VARCHAR(n)(utf8mb4) | VARCHAR(n) | NVARCHAR2(n) | NVARCHAR(n) | TEXT | SQL Server NVARCHAR 最大 4000 字符(或 MAX) | SQL Server 里存中文务必用 NVARCHAR,字面量要写 N'中文' |
| 字符串 | 小文本(255 字节内) | TINYTEXT | TEXT | VARCHAR2(255) | VARCHAR(255) | TEXT | 最大 255 字节(约 85 个中文字符) | MySQL 特有,实践中直接用 VARCHAR(255) 更方便,可建普通索引 |
| 字符串 | 中等文本(64KB) | TEXT | TEXT | CLOB | VARCHAR(MAX) | TEXT | 最大 65,535 字节(约 2.1 万中文字符) | 存文章正文、备注、日志。MySQL 中 TEXT 不能设默认值,索引需指定前缀长度 |
| 字符串 | 长文本(16MB) | MEDIUMTEXT | TEXT | CLOB | VARCHAR(MAX) | TEXT | 最大 16,777,215 字节(16MB) | 存富文本 HTML、大段 JSON 字符串、爬虫原文 |
| 字符串 | 超长文本(4GB) | LONGTEXT | TEXT(≤1GB) | CLOB | VARCHAR(MAX)(≤2GB) | TEXT | MySQL 最大 4GB,但受 max_allowed_packet 限制 | 超大内容建议存对象存储只在库里放 URL,避免拖垮备份与主从同步 |
| 字符串 | 单字符标志 | CHAR(1) | CHAR(1) | CHAR(1) | CHAR(1) | TEXT | 1 字节(非 Unicode) | Oracle 常用 CHAR(1) 存 'Y'/'N' 代替布尔,迁移到 PG 要转成 BOOLEAN |
| 字符串 | 固定格式编码 | CHAR(18) | CHAR(18) | CHAR(18) | CHAR(18) | TEXT | 身份证 18 位、邮编 6 位、银行卡 16~19 位 | 长度固定用 CHAR 更省空间;含字母 X 的身份证不能用数字类型 |
| 字符串 | 数组 / 多值字段 | JSON 数组 | TEXT[] / INT[] | VARRAY / 嵌套表 | NVARCHAR(MAX) + JSON | TEXT(JSON) | PG 数组支持任意维度,可建 GIN 索引 | PostgreSQL 原生数组是独门优势;其他库建议改为关联表或 JSON |
| 日期时间 | 纯日期 | DATE | DATE | DATE(含时分秒!) | DATE | TEXT / NUMERIC | MySQL 1000-01-01 ~ 9999-12-31,3 字节;PG 4 字节 | Oracle 的 DATE 实际带时分秒,迁移到其他库的 DATE 会静默丢失时间部分 |
| 日期时间 | 纯时间 | TIME | TIME / TIMETZ | INTERVAL DAY TO SECOND | TIME(n) | TEXT | MySQL TIME 为 -838:59:59 ~ 838:59:59(可表示时长) | Oracle 没有纯 TIME 类型,需要用 INTERVAL 或字符串模拟 |
| 日期时间 | 日期时间(无时区) | DATETIME | TIMESTAMP | TIMESTAMP | DATETIME2 | TEXT(ISO8601) | MySQL 1000-01-01 ~ 9999-12-31,5~8 字节;DATETIME2 精度可到 100 纳秒 | SQL Server 新项目一律用 DATETIME2,旧的 DATETIME 精度只有 3.33 毫秒 |
| 日期时间 | 带时区时间戳 | TIMESTAMP(隐式转换) | TIMESTAMPTZ | TIMESTAMP WITH TIME ZONE | DATETIMEOFFSET | TEXT(带偏移) | MySQL TIMESTAMP 仅 1970-01-01 ~ 2038-01-19,4 字节 | MySQL TIMESTAMP 有 2038 年问题;跨时区业务优先选 PG 的 TIMESTAMPTZ |
| 日期时间 | 本地时区时间戳 | 无 | TIMESTAMPTZ | TIMESTAMP WITH LOCAL TIME ZONE | 无 | 无 | 存储时统一转 UTC,读取时按会话时区还原 | Oracle 特有,迁移时统一改为「存 UTC + 应用层转换」的方案最省心 |
| 日期时间 | 年份 | YEAR | SMALLINT | NUMBER(4) | SMALLINT | INTEGER | MySQL YEAR:1901 ~ 2155,1 字节 | MySQL 独有,可读性一般,跨库项目直接用 SMALLINT 更通用 |
| 日期时间 | Unix 时间戳(秒) | INT UNSIGNED / BIGINT | BIGINT | NUMBER(19) | BIGINT | INTEGER | 秒级需 4~8 字节;32 位有符号在 2038 年溢出 | 跨语言、跨时区最省事的方案;一律用 BIGINT 避免 2038 问题 |
| 日期时间 | 毫秒 / 微秒精度 | DATETIME(3) ~ DATETIME(6) | TIMESTAMP(6) | TIMESTAMP(9) | DATETIME2(7) | TEXT | MySQL 最高微秒(6 位);Oracle 最高纳秒(9 位) | MySQL 5.6.4+ 才支持小数秒;不写精度默认是 0,会静默丢掉毫秒 |
| 日期时间 | 时间间隔 | INT(存秒数) | INTERVAL | INTERVAL YEAR TO MONTH | INT(存秒数) | INTEGER | PG INTERVAL 为 16 字节,可表示 ±1.78 亿年 | 只有 PG 与 Oracle 有原生间隔类型,其余库统一存秒数整型 |
| 日期时间 | 自动更新时间戳 | TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 触发器实现 | 触发器实现 | 触发器 / ROWVERSION | 触发器实现 | — | MySQL 的自动更新列很方便,但迁移到其他库都需要改写成触发器 |
| 二进制 | 定长二进制 | BINARY(n) | BYTEA | RAW(n) | BINARY(n) | BLOB | MySQL 最大 255 字节;Oracle RAW 最大 2000 字节 | 存哈希、加密密钥。PG 没有定长二进制,一律 BYTEA |
| 二进制 | 变长二进制 | VARBINARY(n) | BYTEA | RAW(2000) / BLOB | VARBINARY(n) | BLOB | MySQL 行内最大 65535 字节 | 存小图标、序列化对象、二进制协议报文 |
| 二进制 | 小二进制(255B) | TINYBLOB | BYTEA | RAW(255) | VARBINARY(255) | BLOB | 最大 255 字节 | MySQL 特有;实际用 VARBINARY 更灵活 |
| 二进制 | 中二进制(64KB) | BLOB | BYTEA | BLOB | VARBINARY(MAX) | BLOB | 最大 65,535 字节 | 存小文件、缩略图;大量二进制建议外置到对象存储 |
| 二进制 | 大二进制(16MB / 4GB) | MEDIUMBLOB / LONGBLOB | BYTEA(≤1GB)/ 大对象 | BLOB | VARBINARY(MAX) / FILESTREAM | BLOB | MEDIUMBLOB 16MB;LONGBLOB 4GB | 数据库存大文件会严重拖慢备份和复制,强烈建议只存路径或 URL |
| 二进制 | 哈希摘要 | BINARY(16) / CHAR(32) | BYTEA / CHAR(32) | RAW(16) | BINARY(16) | BLOB | MD5 16 字节 / 32 位十六进制;SHA-256 32 字节 / 64 位十六进制 | 用二进制存比十六进制字符串省一半空间,索引也更快 |
| 布尔 | 布尔值 | TINYINT(1) / BOOL | BOOLEAN | NUMBER(1) / CHAR(1) | BIT | INTEGER 0/1 | 1 字节(SQL Server 的 BIT 多列会打包成 1 字节) | MySQL 的 BOOLEAN 只是 TINYINT(1) 的别名,能存 0~127 任意值,需要靠应用层约束 |
| 布尔 | 三态布尔(可为 NULL) | TINYINT(1) NULL | BOOLEAN NULL | CHAR(1) NULL | BIT NULL | INTEGER NULL | TRUE / FALSE / UNKNOWN(NULL) | 「未填写」与「否」要区分时才用可空布尔,否则一律 NOT NULL DEFAULT 0 |
| 布尔 | 多标志位打包 | SET / BIT(n) | BIT VARYING / BOOLEAN[] | NUMBER 位运算 | INT 位运算 | INTEGER | 32 位整数可存 32 个开关 | 位运算查询无法走索引,标志位少于 8 个时拆成独立布尔列更好 |
| JSON | JSON 文档 | JSON | JSON | JSON(21c+)/ CLOB | NVARCHAR(MAX) + ISJSON | TEXT + json1 | MySQL JSON 单值最大 1GB;PG JSON 上限 1GB | PG 的 JSON 保留原始文本与键顺序,JSONB 才是解析后的二进制格式 |
| JSON | 二进制 JSON(可索引) | JSON(内部二进制) | JSONB | JSON(OSON 格式) | 计算列 + 索引 | JSONB(3.45+) | JSONB 会去重键、丢弃空白与键顺序,查询更快但写入略慢 | PostgreSQL 存 JSON 一律选 JSONB,可以建 GIN 索引做高效包含查询 |
| JSON | JSON 字段索引 | 生成列 + 普通索引 | GIN / 表达式索引 | 多值索引 / 函数索引 | 计算列 + 索引 | 表达式索引 | — | MySQL 不能直接给 JSON 列建索引,必须先抽成 STORED 生成列 |
| XML | XML 文档 | TEXT(无原生类型) | XML | XMLTYPE | XML | TEXT | SQL Server XML 最大 2GB,支持 XQuery 与 XML 索引 | 新项目优先用 JSON;只有对接老系统或行业报文时才用 XML |
| 枚举 | 单选枚举 | ENUM('a','b') | CREATE TYPE ... AS ENUM | VARCHAR2 + CHECK | VARCHAR + CHECK | TEXT + CHECK | MySQL ENUM 最多 65,535 个成员,内部按 1~2 字节整数存储 | MySQL ENUM 增删值要 ALTER TABLE 锁表;跨库项目建议用小整数 + 字典表 |
| 枚举 | 多选集合 | SET('a','b') | TEXT[] / BIT VARYING | 关联表 | 关联表 | 关联表 | MySQL SET 最多 64 个成员,占 1~8 字节 | MySQL 独有且难以移植,推荐改为多对多关联表,可索引也便于统计 |
| 枚举 | 字典表外键(推荐) | SMALLINT + FK | SMALLINT + FK | NUMBER(5) + FK | SMALLINT + FK | INTEGER + FK | 2 字节,可支持 3 万多个枚举值 | 最通用、最易扩展的做法:状态码存整数,含义放字典表,增删值无需改表结构 |
| UUID | UUID / GUID | BINARY(16) / CHAR(36) | UUID | RAW(16) + SYS_GUID() | UNIQUEIDENTIFIER | TEXT / BLOB | 16 字节二进制 / 36 字符文本(含 4 个连字符) | MySQL 用 BINARY(16) 比 CHAR(36) 省 55% 空间;配合 UUID_TO_BIN(x,1) 重排时间位可大幅改善索引局部性 |
| 网络 | IPv4 地址 | INT UNSIGNED / VARCHAR(15) | INET / CIDR | VARCHAR2(15) | VARCHAR(15) / BIGINT | TEXT | 点分十进制最长 15 字符;整数形式 4 字节 | MySQL 用 INET_ATON() 转成整数存,范围查询能走索引;PG 直接用 INET 最优雅 |
| 网络 | IPv6 地址 | VARBINARY(16) / VARCHAR(45) | INET | VARCHAR2(45) | VARCHAR(45) | TEXT | 文本最长 45 字符;二进制固定 16 字节 | 要同时兼容 IPv4/IPv6 时,MySQL 用 VARBINARY(16) + INET6_ATON() |
| 网络 | MAC 地址 | BIGINT / CHAR(17) | MACADDR / MACADDR8 | VARCHAR2(17) | CHAR(17) | TEXT | MACADDR 6 字节;文本形式 17 字符 | PostgreSQL 有原生类型并自动规范化格式,其他库要在应用层统一大小写与分隔符 |
| 空间 | 地理坐标点 | POINT / GEOMETRY | POINT / PostGIS GEOMETRY | SDO_GEOMETRY | GEOGRAPHY / GEOMETRY | SpatiaLite 扩展 | MySQL POINT 占 25 字节,支持 SRID 与空间索引 | 严肃的 GIS 场景首选 PostgreSQL + PostGIS,功能远超其他库 |
| 空间 | 经纬度(简单存储) | DECIMAL(10,7) | NUMERIC(10,7) | NUMBER(10,7) | DECIMAL(10,7) | REAL | 纬度 -90 ~ 90,经度 -180 ~ 180;7 位小数约 1.1 厘米精度 | 不做空间检索时,用两个 DECIMAL 列比空间类型更简单,也方便建联合索引 |
| 全文 | 全文检索 | FULLTEXT 索引 | TSVECTOR + GIN | Oracle Text | 全文索引 | FTS5 虚拟表 | PG TSVECTOR 上限约 1MB | 中文分词各库支持都一般,量大时建议接 Elasticsearch |
| 主键自增 | 自增整数主键 | INT AUTO_INCREMENT | SERIAL / GENERATED AS IDENTITY | SEQUENCE / IDENTITY(12c+) | INT IDENTITY(1,1) | INTEGER PRIMARY KEY | 4 字节,最大约 21 亿 | PG 新项目推荐标准 SQL 的 GENERATED ALWAYS AS IDENTITY 而非老式 SERIAL |
| 主键自增 | 自增大整数主键 | BIGINT AUTO_INCREMENT | BIGSERIAL | NUMBER(19) + SEQUENCE | BIGINT IDENTITY(1,1) | INTEGER PRIMARY KEY | 8 字节,最大约 922 亿亿 | 数据量可能过亿的表直接上 BIGINT,后期改类型要锁表非常痛苦 |
| 主键自增 | 序列对象 | 无(8.0 可用窗口函数模拟) | CREATE SEQUENCE | CREATE SEQUENCE | CREATE SEQUENCE | 无 | 可设置起始值、步长、缓存、循环 | MySQL 与 SQLite 没有独立序列对象,多表共享编号只能自建号段表 |
| 主键自增 | 有序 UUID 主键 | BINARY(16) + UUID_TO_BIN(x,1) | UUID(UUIDv7) | RAW(16) | UNIQUEIDENTIFIER + NEWSEQUENTIALID() | BLOB | 16 字节 | 随机 UUID 做聚簇索引主键会造成严重页分裂,务必使用时间有序版本 |
| 主键自增 | 分布式 ID(雪花算法) | BIGINT | BIGINT | NUMBER(19) | BIGINT | INTEGER | 64 位:1 符号位 + 41 时间位 + 10 机器位 + 12 序列位 | 兼顾有序与全局唯一,是分库分表场景的主流方案;注意 JS 前端精度只有 53 位,需转字符串传输 |
| 其他 | 行版本 / 乐观锁 | INT version 列 | 系统列 xmin | ORA_ROWSCN | ROWVERSION | INTEGER | ROWVERSION 固定 8 字节,库内自动递增 | SQL Server 的 TIMESTAMP 其实就是 ROWVERSION,和时间毫无关系,极易被误解 |
| 其他 | 金额(以「分」存储) | BIGINT | BIGINT | NUMBER(19) | BIGINT | INTEGER | 8 字节,可表示约 ¥9,223 万亿 | 互联网支付系统常用做法:全链路以最小货币单位(分)的整数流转,完全规避小数误差 |
| 其他 | 外部文件引用 | VARCHAR(512) 存路径 | TEXT / 大对象 OID | BFILE | FILESTREAM / FileTable | TEXT | — | 推荐统一存对象存储的 URL 或 Key,数据库只做元数据管理 |
| 其他 | 枚举状态 + 备注组合 | TINYINT + VARCHAR(255) | SMALLINT + TEXT | NUMBER(3) + VARCHAR2(255) | TINYINT + NVARCHAR(255) | INTEGER + TEXT | 1 + 255 字节 | 状态用整数便于索引与统计,备注用变长文本,两者分开存最灵活 |
| 其他 | 软删除标记 | TINYINT(1) / DATETIME | BOOLEAN / TIMESTAMPTZ | CHAR(1) / DATE | BIT / DATETIME2 | INTEGER / TEXT | 1 字节或 8 字节 | 用可空的 deleted_at 时间戳比布尔标志信息量更大,还能记录删除时间 |
| 其他 | 大字段是否行外存储 | 溢出页(DYNAMIC 行格式) | TOAST 自动压缩外置 | LOB 段外置 | 行外 LOB 数据页 | 溢出页 | PG 超过约 2KB 自动 TOAST | 大字段单独拆表可显著提升主表扫描性能,尤其是 MySQL InnoDB |
| 其他 | 字符集与排序规则 | utf8mb4_0900_ai_ci | 数据库级 UTF8 + COLLATE | AL32UTF8 | 列级 COLLATE | 仅 BINARY / NOCASE / RTRIM | utf8mb4 每字符最多 4 字节 | MySQL 的 utf8 是只支持 3 字节的假 UTF-8,存 emoji 必须用 utf8mb4 |
MySQL 里 BOOLEAN / BOOL 只是 TINYINT(1) 的语法糖,字段实际能存 -128 ~ 127 的任意整数,
括号里的 1 只是显示宽度而非取值约束。很多 ORM(如 JDBC 的 tinyInt1isBit 参数)会把 TINYINT(1) 自动映射成 boolean,
于是数据库里存的 2、3 到了 Java 层全变成 true。迁移到 PostgreSQL 的真 BOOLEAN 时,
非 0/1 的脏数据会直接导致转换失败,必须先跑一遍 UPDATE t SET flag = 1 WHERE flag <> 0 清洗。
Oracle 里不带参数的 NUMBER 是浮动精度,能存最多 38 位有效数字的整数或小数。
用工具直接迁到 MySQL 时,常被默认映射为 DECIMAL(10,0) 或 BIGINT,
结果小数部分被静默截断、超长数字直接溢出报错。
另外 NUMBER(p) 等价于 NUMBER(p,0),会对小数做四舍五入而不是报错,非常隐蔽。
正确做法是迁移前用 SELECT MAX(LENGTH(TO_CHAR(col))) 之类的语句探明真实精度,再显式指定目标类型。
DATETIME 是「墙上时间」,存什么读什么,不做任何时区转换,范围 1000 ~ 9999 年,占 5~8 字节。
TIMESTAMP 会在写入时把会话时区转成 UTC 存储、读取时再转回会话时区,
范围只有 1970-01-01 ~ 2038-01-19(著名的 2038 问题),占 4 字节。
同一条记录,不同时区的连接读到的 TIMESTAMP 值是不一样的,而 DATETIME 完全一致。
跨国业务建议统一「DATETIME 存 UTC 时间 + 应用层转换」或直接用 BIGINT 存毫秒时间戳。
SQLite 只有 5 种存储类:NULL、INTEGER、REAL、TEXT、BLOB。
你写的 VARCHAR(50)、DATETIME 都只是类型亲和性(Type Affinity)的提示,
长度完全不生效,往 VARCHAR(10) 里插 1 万个字符也不会报错。
更要注意的是 SQLite 允许在任何列里存任何类型的值(除非声明为 STRICT 表),
于是同一列可能同时存在数字 123 和字符串 '123',两者比较结果为不相等。
SQLite 3.37+ 支持 CREATE TABLE ... STRICT 强类型表,新项目建议开启。
Oracle 的 DATE 精确到秒,而 MySQL / PostgreSQL / SQL Server 的 DATE 只有年月日。
从 Oracle 迁出时如果无脑映射成 DATE,所有时间部分会被清零,订单时间、日志时间全部退化成 00:00:00,
而且这种丢失不会有任何报错。正确映射目标是 DATETIME / TIMESTAMP / DATETIME2。
Oracle 把空字符串 '' 视为 NULL,其他所有数据库都把它当成长度为 0 的有效字符串。
因此 Oracle 里 WHERE col = '' 永远查不到任何行,必须写 WHERE col IS NULL。
迁移时带 NOT NULL 约束的列如果原本存的是空串,插入 Oracle 会直接违反约束。
PostgreSQL 会把未加引号的标识符全部转成小写,Oracle 则全部转成大写,
MySQL 在 Linux 上表名区分大小写、在 Windows 上不区分。
从 MySQL 的 userName 迁到 PG 后会变成 username,程序里写 "userName" 加引号查询就会报「列不存在」。
结论:库表字段一律用小写加下划线命名,这是跨库最安全的约定。
MySQL 的 AUTO_INCREMENT、SQL Server 的 IDENTITY、PostgreSQL 的 SERIAL(本质是序列 + 默认值)、
Oracle 12c 之前只能靠序列 + 触发器,四者语法完全不同。
迁移后还要记得重置序列的当前值,否则导入历史数据后新增记录会主键冲突:
PG 用 SELECT setval('t_id_seq', (SELECT MAX(id) FROM t)),
MySQL 用 ALTER TABLE t AUTO_INCREMENT = n。
MySQL 的 utf8 字符集最多只用 3 字节,无法存储 emoji 和部分生僻汉字(它们需要 4 字节)。
必须使用 utf8mb4。注意 utf8mb4 下 VARCHAR(255) 最多占 1020 字节,
而 InnoDB 单个索引键默认上限是 3072 字节,给长 VARCHAR 建联合索引时很容易超限。
SQL Server 里的 TIMESTAMP(新名字 ROWVERSION)是一个行版本号,
8 字节二进制,每次行更新时自动递增,和日期时间毫无关系,用于乐观并发控制。
要存时间请用 DATETIME2。这是从其他库转过来的开发者最常踩的命名陷阱之一。
| 业务场景 | 推荐类型 | 为什么这样选 |
|---|---|---|
| 手机号 | VARCHAR(20) / CHAR(11) |
绝不要用整数。手机号不参与算术运算;国际号码含 + 与国家码;某些地区号码以 0 开头,用数字类型会丢掉前导零。留 20 位可容纳国际号与分机号。 |
| 金额 / 价格 / 余额 | DECIMAL(19,4),或 BIGINT 存「分」 |
绝对不要用 FLOAT / DOUBLE。二进制浮点无法精确表示 0.1,累加会出现 0.30000000000000004 这类误差,对账必然对不平。DECIMAL 是十进制定点,运算完全精确。支付系统更常用整数存最小货币单位。 |
| IP 地址 | PG 用 INET;MySQL 用 INT UNSIGNED(IPv4)或 VARBINARY(16)(兼容 IPv6) |
整数形式只占 4 字节且支持高效的网段范围查询(BETWEEN 可走索引)。若仅做展示不做检索,VARCHAR(45) 也可接受。 |
| 大段文本(文章 / 富文本) | MySQL TEXT / MEDIUMTEXT;PG TEXT;Oracle CLOB |
大字段会撑大数据页、拖慢全表扫描。建议把正文单独拆到附表,主表只保留摘要与元数据。PG 的 TEXT 无性能损失可直接用。 |
| 主键 ID | 单机 BIGINT 自增;分布式雪花 ID 或 UUIDv7 |
预估会过亿的表直接上 BIGINT,后期从 INT 改 BIGINT 需要长时间锁表。随机 UUID 做聚簇索引会造成页分裂,务必用时间有序版本。 |
| 状态 / 类型枚举 | TINYINT / SMALLINT + 字典表 |
整数索引效率最高、存储最省,新增状态无需 ALTER TABLE。MySQL 的 ENUM 改值要锁表且难以跨库迁移。 |
| 创建时间 / 更新时间 | DATETIME(3) 存 UTC,或 PG 的 TIMESTAMPTZ |
避开 MySQL TIMESTAMP 的 2038 问题;统一存 UTC 可彻底规避多时区混乱;毫秒精度便于排序与幂等判重。 |
| 密码 | CHAR(60)(bcrypt)或 VARCHAR(255) |
只存加盐哈希,永不存明文。bcrypt 结果固定 60 字符;预留 255 位可兼容未来更换 argon2 等更长的算法。 |
| 邮箱 | VARCHAR(254) |
RFC 5321 规定邮件地址最大长度为 254 字符。需要唯一索引时注意 utf8mb4 下的索引长度限制。 |
| 身份证号 | CHAR(18) |
长度固定;末位校验码可能是字母 X,不能用数字类型;一代身份证 15 位时可用 VARCHAR(18)。属敏感信息,建议加密存储。 |
| 经纬度 | DECIMAL(10,7) 两列,或空间类型 POINT |
7 位小数约 1.1 厘米精度,足够绝大多数场景。需要「附近的人」这类检索时才上 PostGIS 或 MySQL 空间索引。 |
| 订单号 / 流水号 | CHAR(n) 或 VARCHAR(32) |
业务编号通常含日期前缀与字母,长度固定时用 CHAR 更省空间且比较更快。不要用自增 ID 直接对外暴露,会泄露业务量。 |
| 是否删除 / 开关标志 | PG BOOLEAN;MySQL TINYINT(1) NOT NULL DEFAULT 0 |
务必加 NOT NULL 与默认值,避免三态逻辑带来的查询遗漏。用 deleted_at 时间戳代替布尔可额外记录删除时间。 |
| 半结构化扩展字段 | PG JSONB;MySQL JSON |
适合字段不固定的扩展属性。但高频查询条件不要放 JSON 里,应抽成独立列或生成列并建索引。 |
| 文件 / 图片 | VARCHAR(512) 存 URL 或对象存储 Key |
把二进制塞进数据库会让备份体积暴涨、主从同步延迟飙升。数据库只做元数据索引,文件本体交给 OSS / S3 / MinIO。 |
| 计数器 / 浏览量 | BIGINT UNSIGNED(MySQL)/ BIGINT |
高并发计数建议先在 Redis 累加再定期落库,避免行锁竞争成为瓶颈。 |
| 版本号 / 乐观锁 | INT 自增列 或 SQL Server ROWVERSION |
更新时带上 WHERE version = ? 并 SET version = version + 1,用影响行数判断是否冲突。 |
TINYINT 就别用 INT,能 VARCHAR(50) 就别 VARCHAR(255)。字段越窄,单页装的行越多,索引与缓存效率越高。NOT IN 的行为变得反直觉,还额外占用 NULL 标记位。