🗄️ SQL 数据类型对照表 — MySQL / PostgreSQL / Oracle / SQL Server / SQLite

五大主流数据库完整数据类型对照速查表,覆盖整数、浮点定点、字符串、日期时间、二进制、布尔、 JSON、枚举、UUID 与网络地址等 11 大类共 60+ 种类型, 每行标注取值范围、占用字节与选型建议。 支持实时搜索过滤任选两个数据库左右对照高亮, 并附常见跨库迁移坑位字段选型决策指引

分类 用途 / 通用类型 MySQL PostgreSQL Oracle SQL Server SQLite 取值范围 / 占用字节 使用建议
整数极小整数(1 字节)TINYINTSMALLINTNUMBER(3)TINYINTINTEGER-128 ~ 127;1 字节。SQL Server 的 TINYINT 是无符号 0 ~ 255存年龄、状态码、小型枚举值。PostgreSQL 没有 1 字节整数,最小就是 SMALLINT
整数无符号极小整数TINYINT UNSIGNEDSMALLINT + CHECKNUMBER(3) + CHECKTINYINTINTEGER0 ~ 255;1 字节只有 MySQL 与 SQL Server 原生支持,其他库需要用 CHECK 约束模拟无符号
整数小整数(2 字节)SMALLINTSMALLINT / INT2NUMBER(5)SMALLINTINTEGER-32,768 ~ 32,767;2 字节存年份、数量、较小的计数器,比 INT 省一半空间
整数中整数(3 字节)MEDIUMINTINTEGERNUMBER(7)INTINTEGER-8,388,608 ~ 8,388,607;3 字节MySQL 独有类型,迁移到其他库统一升为 4 字节 INT
整数标准整数(4 字节)INT / INTEGERINTEGER / INT4NUMBER(10)INTINTEGER-2,147,483,648 ~ 2,147,483,647;4 字节最常用的整数类型,绝大多数计数、外键、ID 场景的默认选择
整数大整数(8 字节)BIGINTBIGINT / INT8NUMBER(19)BIGINTINTEGER约 -9.22×10¹⁸ ~ 9.22×10¹⁸;8 字节存雪花 ID、Unix 毫秒时间戳、以「分」为单位的金额、超大计数
整数无符号大整数BIGINT UNSIGNEDNUMERIC(20)NUMBER(20)DECIMAL(20,0)INTEGER(有符号)0 ~ 18,446,744,073,709,551,615;8 字节迁移高危项:只有 MySQL 有无符号 BIGINT,其他库要降级为定点数,性能与索引都会变差
整数位串 / 标志位BIT(n)BIT(n) / BIT VARYINGRAW / NUMBERBINARY / INTINTEGERn 位二进制,1 ~ 64 位;按位打包存储存权限位图、多选标志。可读性差,字段少时建议改用多个布尔列
浮点定点单精度浮点FLOATREAL / FLOAT4BINARY_FLOATREAL / FLOAT(24)REAL约 ±3.4×10³⁸,约 7 位有效数字;4 字节只适合science/传感器等可容忍误差的场景,绝对不要存金额
浮点定点双精度浮点DOUBLEDOUBLE PRECISION / FLOAT8BINARY_DOUBLEFLOAT(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.0001PG 的 MONEY 与 SQL Server 的 MONEY 都依赖区域设置,跨库不安全,统一用 DECIMAL(19,4) 最稳妥
浮点定点超高精度大数DECIMAL(65,30)NUMERIC(无参)NUMBER(38,x)DECIMAL(38,x)TEXTMySQL 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)REALp ≤ 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) / BPCHARCHAR(n)CHAR(n)TEXTMySQL 最大 255 字符;Oracle 2000 字节;SQL Server 8000 字节只用于长度真正固定的值(性别、国家码、MD5)。不足位会补空格,是常见的比较陷阱
字符串变长字符串(最常用)VARCHAR(n)VARCHAR(n)VARCHAR2(n)VARCHAR(n) / NVARCHAR(n)TEXTMySQL 行内最大 65535 字节;Oracle 4000 字节(12c 起可 32767);SQL Server 8000Oracle 里请用 VARCHAR2 而不是 VARCHAR,后者语义保留可能变化
字符串不限长度文本TEXT / LONGTEXTTEXTCLOBVARCHAR(MAX)TEXTPG TEXT 上限约 1GB;SQL Server MAX 为 2GB;Oracle CLOB 可达 128TBPostgreSQL 的 TEXT 与 VARCHAR 性能完全一致,PG 里可以放心用 TEXT
字符串Unicode 定长CHAR(n)(utf8mb4)CHAR(n)NCHAR(n)NCHAR(n)TEXTNCHAR 按字符计数,每字符 2 字节(UTF-16)SQL Server 与 Oracle 需要区分 N 前缀类型,MySQL/PG/SQLite 默认就是 Unicode
字符串Unicode 变长VARCHAR(n)(utf8mb4)VARCHAR(n)NVARCHAR2(n)NVARCHAR(n)TEXTSQL Server NVARCHAR 最大 4000 字符(或 MAX)SQL Server 里存中文务必用 NVARCHAR,字面量要写 N'中文'
字符串小文本(255 字节内)TINYTEXTTEXTVARCHAR2(255)VARCHAR(255)TEXT最大 255 字节(约 85 个中文字符)MySQL 特有,实践中直接用 VARCHAR(255) 更方便,可建普通索引
字符串中等文本(64KB)TEXTTEXTCLOBVARCHAR(MAX)TEXT最大 65,535 字节(约 2.1 万中文字符)存文章正文、备注、日志。MySQL 中 TEXT 不能设默认值,索引需指定前缀长度
字符串长文本(16MB)MEDIUMTEXTTEXTCLOBVARCHAR(MAX)TEXT最大 16,777,215 字节(16MB)存富文本 HTML、大段 JSON 字符串、爬虫原文
字符串超长文本(4GB)LONGTEXTTEXT(≤1GB)CLOBVARCHAR(MAX)(≤2GB)TEXTMySQL 最大 4GB,但受 max_allowed_packet 限制超大内容建议存对象存储只在库里放 URL,避免拖垮备份与主从同步
字符串单字符标志CHAR(1)CHAR(1)CHAR(1)CHAR(1)TEXT1 字节(非 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) + JSONTEXT(JSON)PG 数组支持任意维度,可建 GIN 索引PostgreSQL 原生数组是独门优势;其他库建议改为关联表或 JSON
日期时间纯日期DATEDATEDATE(含时分秒!)DATETEXT / NUMERICMySQL 1000-01-01 ~ 9999-12-31,3 字节;PG 4 字节Oracle 的 DATE 实际带时分秒,迁移到其他库的 DATE 会静默丢失时间部分
日期时间纯时间TIMETIME / TIMETZINTERVAL DAY TO SECONDTIME(n)TEXTMySQL TIME 为 -838:59:59 ~ 838:59:59(可表示时长)Oracle 没有纯 TIME 类型,需要用 INTERVAL 或字符串模拟
日期时间日期时间(无时区)DATETIMETIMESTAMPTIMESTAMPDATETIME2TEXT(ISO8601)MySQL 1000-01-01 ~ 9999-12-31,5~8 字节;DATETIME2 精度可到 100 纳秒SQL Server 新项目一律用 DATETIME2,旧的 DATETIME 精度只有 3.33 毫秒
日期时间带时区时间戳TIMESTAMP(隐式转换)TIMESTAMPTZTIMESTAMP WITH TIME ZONEDATETIMEOFFSETTEXT(带偏移)MySQL TIMESTAMP 仅 1970-01-01 ~ 2038-01-19,4 字节MySQL TIMESTAMP 有 2038 年问题;跨时区业务优先选 PG 的 TIMESTAMPTZ
日期时间本地时区时间戳TIMESTAMPTZTIMESTAMP WITH LOCAL TIME ZONE存储时统一转 UTC,读取时按会话时区还原Oracle 特有,迁移时统一改为「存 UTC + 应用层转换」的方案最省心
日期时间年份YEARSMALLINTNUMBER(4)SMALLINTINTEGERMySQL YEAR:1901 ~ 2155,1 字节MySQL 独有,可读性一般,跨库项目直接用 SMALLINT 更通用
日期时间Unix 时间戳(秒)INT UNSIGNED / BIGINTBIGINTNUMBER(19)BIGINTINTEGER秒级需 4~8 字节;32 位有符号在 2038 年溢出跨语言、跨时区最省事的方案;一律用 BIGINT 避免 2038 问题
日期时间毫秒 / 微秒精度DATETIME(3) ~ DATETIME(6)TIMESTAMP(6)TIMESTAMP(9)DATETIME2(7)TEXTMySQL 最高微秒(6 位);Oracle 最高纳秒(9 位)MySQL 5.6.4+ 才支持小数秒;不写精度默认是 0,会静默丢掉毫秒
日期时间时间间隔INT(存秒数)INTERVALINTERVAL YEAR TO MONTHINT(存秒数)INTEGERPG INTERVAL 为 16 字节,可表示 ±1.78 亿年只有 PG 与 Oracle 有原生间隔类型,其余库统一存秒数整型
日期时间自动更新时间戳TIMESTAMP ON UPDATE CURRENT_TIMESTAMP触发器实现触发器实现触发器 / ROWVERSION触发器实现MySQL 的自动更新列很方便,但迁移到其他库都需要改写成触发器
二进制定长二进制BINARY(n)BYTEARAW(n)BINARY(n)BLOBMySQL 最大 255 字节;Oracle RAW 最大 2000 字节存哈希、加密密钥。PG 没有定长二进制,一律 BYTEA
二进制变长二进制VARBINARY(n)BYTEARAW(2000) / BLOBVARBINARY(n)BLOBMySQL 行内最大 65535 字节存小图标、序列化对象、二进制协议报文
二进制小二进制(255B)TINYBLOBBYTEARAW(255)VARBINARY(255)BLOB最大 255 字节MySQL 特有;实际用 VARBINARY 更灵活
二进制中二进制(64KB)BLOBBYTEABLOBVARBINARY(MAX)BLOB最大 65,535 字节存小文件、缩略图;大量二进制建议外置到对象存储
二进制大二进制(16MB / 4GB)MEDIUMBLOB / LONGBLOBBYTEA(≤1GB)/ 大对象BLOBVARBINARY(MAX) / FILESTREAMBLOBMEDIUMBLOB 16MB;LONGBLOB 4GB数据库存大文件会严重拖慢备份和复制,强烈建议只存路径或 URL
二进制哈希摘要BINARY(16) / CHAR(32)BYTEA / CHAR(32)RAW(16)BINARY(16)BLOBMD5 16 字节 / 32 位十六进制;SHA-256 32 字节 / 64 位十六进制用二进制存比十六进制字符串省一半空间,索引也更快
布尔布尔值TINYINT(1) / BOOLBOOLEANNUMBER(1) / CHAR(1)BITINTEGER 0/11 字节(SQL Server 的 BIT 多列会打包成 1 字节)MySQL 的 BOOLEAN 只是 TINYINT(1) 的别名,能存 0~127 任意值,需要靠应用层约束
布尔三态布尔(可为 NULL)TINYINT(1) NULLBOOLEAN NULLCHAR(1) NULLBIT NULLINTEGER NULLTRUE / FALSE / UNKNOWN(NULL)「未填写」与「否」要区分时才用可空布尔,否则一律 NOT NULL DEFAULT 0
布尔多标志位打包SET / BIT(n)BIT VARYING / BOOLEAN[]NUMBER 位运算INT 位运算INTEGER32 位整数可存 32 个开关位运算查询无法走索引,标志位少于 8 个时拆成独立布尔列更好
JSONJSON 文档JSONJSONJSON(21c+)/ CLOBNVARCHAR(MAX) + ISJSONTEXT + json1MySQL JSON 单值最大 1GB;PG JSON 上限 1GBPG 的 JSON 保留原始文本与键顺序,JSONB 才是解析后的二进制格式
JSON二进制 JSON(可索引)JSON(内部二进制)JSONBJSON(OSON 格式)计算列 + 索引JSONB(3.45+)JSONB 会去重键、丢弃空白与键顺序,查询更快但写入略慢PostgreSQL 存 JSON 一律选 JSONB,可以建 GIN 索引做高效包含查询
JSONJSON 字段索引生成列 + 普通索引GIN / 表达式索引多值索引 / 函数索引计算列 + 索引表达式索引MySQL 不能直接给 JSON 列建索引,必须先抽成 STORED 生成列
XMLXML 文档TEXT(无原生类型)XMLXMLTYPEXMLTEXTSQL Server XML 最大 2GB,支持 XQuery 与 XML 索引新项目优先用 JSON;只有对接老系统或行业报文时才用 XML
枚举单选枚举ENUM('a','b')CREATE TYPE ... AS ENUMVARCHAR2 + CHECKVARCHAR + CHECKTEXT + CHECKMySQL ENUM 最多 65,535 个成员,内部按 1~2 字节整数存储MySQL ENUM 增删值要 ALTER TABLE 锁表;跨库项目建议用小整数 + 字典表
枚举多选集合SET('a','b')TEXT[] / BIT VARYING关联表关联表关联表MySQL SET 最多 64 个成员,占 1~8 字节MySQL 独有且难以移植,推荐改为多对多关联表,可索引也便于统计
枚举字典表外键(推荐)SMALLINT + FKSMALLINT + FKNUMBER(5) + FKSMALLINT + FKINTEGER + FK2 字节,可支持 3 万多个枚举值最通用、最易扩展的做法:状态码存整数,含义放字典表,增删值无需改表结构
UUIDUUID / GUIDBINARY(16) / CHAR(36)UUIDRAW(16) + SYS_GUID()UNIQUEIDENTIFIERTEXT / BLOB16 字节二进制 / 36 字符文本(含 4 个连字符)MySQL 用 BINARY(16)CHAR(36) 省 55% 空间;配合 UUID_TO_BIN(x,1) 重排时间位可大幅改善索引局部性
网络IPv4 地址INT UNSIGNED / VARCHAR(15)INET / CIDRVARCHAR2(15)VARCHAR(15) / BIGINTTEXT点分十进制最长 15 字符;整数形式 4 字节MySQL 用 INET_ATON() 转成整数存,范围查询能走索引;PG 直接用 INET 最优雅
网络IPv6 地址VARBINARY(16) / VARCHAR(45)INETVARCHAR2(45)VARCHAR(45)TEXT文本最长 45 字符;二进制固定 16 字节要同时兼容 IPv4/IPv6 时,MySQL 用 VARBINARY(16) + INET6_ATON()
网络MAC 地址BIGINT / CHAR(17)MACADDR / MACADDR8VARCHAR2(17)CHAR(17)TEXTMACADDR 6 字节;文本形式 17 字符PostgreSQL 有原生类型并自动规范化格式,其他库要在应用层统一大小写与分隔符
空间地理坐标点POINT / GEOMETRYPOINT / PostGIS GEOMETRYSDO_GEOMETRYGEOGRAPHY / GEOMETRYSpatiaLite 扩展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 + GINOracle Text全文索引FTS5 虚拟表PG TSVECTOR 上限约 1MB中文分词各库支持都一般,量大时建议接 Elasticsearch
主键自增自增整数主键INT AUTO_INCREMENTSERIAL / GENERATED AS IDENTITYSEQUENCE / IDENTITY(12c+)INT IDENTITY(1,1)INTEGER PRIMARY KEY4 字节,最大约 21 亿PG 新项目推荐标准 SQL 的 GENERATED ALWAYS AS IDENTITY 而非老式 SERIAL
主键自增自增大整数主键BIGINT AUTO_INCREMENTBIGSERIALNUMBER(19) + SEQUENCEBIGINT IDENTITY(1,1)INTEGER PRIMARY KEY8 字节,最大约 922 亿亿数据量可能过亿的表直接上 BIGINT,后期改类型要锁表非常痛苦
主键自增序列对象无(8.0 可用窗口函数模拟)CREATE SEQUENCECREATE SEQUENCECREATE SEQUENCE可设置起始值、步长、缓存、循环MySQL 与 SQLite 没有独立序列对象,多表共享编号只能自建号段表
主键自增有序 UUID 主键BINARY(16) + UUID_TO_BIN(x,1)UUID(UUIDv7)RAW(16)UNIQUEIDENTIFIER + NEWSEQUENTIALID()BLOB16 字节随机 UUID 做聚簇索引主键会造成严重页分裂,务必使用时间有序版本
主键自增分布式 ID(雪花算法)BIGINTBIGINTNUMBER(19)BIGINTINTEGER64 位:1 符号位 + 41 时间位 + 10 机器位 + 12 序列位兼顾有序与全局唯一,是分库分表场景的主流方案;注意 JS 前端精度只有 53 位,需转字符串传输
其他行版本 / 乐观锁INT version 列系统列 xminORA_ROWSCNROWVERSIONINTEGERROWVERSION 固定 8 字节,库内自动递增SQL Server 的 TIMESTAMP 其实就是 ROWVERSION,和时间毫无关系,极易被误解
其他金额(以「分」存储)BIGINTBIGINTNUMBER(19)BIGINTINTEGER8 字节,可表示约 ¥9,223 万亿互联网支付系统常用做法:全链路以最小货币单位(分)的整数流转,完全规避小数误差
其他外部文件引用VARCHAR(512) 存路径TEXT / 大对象 OIDBFILEFILESTREAM / FileTableTEXT推荐统一存对象存储的 URL 或 Key,数据库只做元数据管理
其他枚举状态 + 备注组合TINYINT + VARCHAR(255)SMALLINT + TEXTNUMBER(3) + VARCHAR2(255)TINYINT + NVARCHAR(255)INTEGER + TEXT1 + 255 字节状态用整数便于索引与统计,备注用变长文本,两者分开存最灵活
其他软删除标记TINYINT(1) / DATETIMEBOOLEAN / TIMESTAMPTZCHAR(1) / DATEBIT / DATETIME2INTEGER / TEXT1 字节或 8 字节用可空的 deleted_at 时间戳比布尔标志信息量更大,还能记录删除时间
其他大字段是否行外存储溢出页(DYNAMIC 行格式)TOAST 自动压缩外置LOB 段外置行外 LOB 数据页溢出页PG 超过约 2KB 自动 TOAST大字段单独拆表可显著提升主表扫描性能,尤其是 MySQL InnoDB
其他字符集与排序规则utf8mb4_0900_ai_ci数据库级 UTF8 + COLLATEAL32UTF8列级 COLLATE仅 BINARY / NOCASE / RTRIMutf8mb4 每字符最多 4 字节MySQL 的 utf8 是只支持 3 字节的假 UTF-8,存 emoji 必须用 utf8mb4

🚧 跨库迁移常见坑位

1. MySQL 的 TINYINT(1) 与 PostgreSQL 的 BOOLEAN

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 清洗。

2. Oracle NUMBER 的精度陷阱

Oracle 里不带参数的 NUMBER浮动精度,能存最多 38 位有效数字的整数或小数。 用工具直接迁到 MySQL 时,常被默认映射为 DECIMAL(10,0)BIGINT, 结果小数部分被静默截断、超长数字直接溢出报错。 另外 NUMBER(p) 等价于 NUMBER(p,0),会对小数做四舍五入而不是报错,非常隐蔽。 正确做法是迁移前用 SELECT MAX(LENGTH(TO_CHAR(col))) 之类的语句探明真实精度,再显式指定目标类型。

3. MySQL DATETIME 与 TIMESTAMP 的时区差异

DATETIME 是「墙上时间」,存什么读什么,不做任何时区转换,范围 1000 ~ 9999 年,占 5~8 字节。 TIMESTAMP 会在写入时把会话时区转成 UTC 存储、读取时再转回会话时区, 范围只有 1970-01-01 ~ 2038-01-19(著名的 2038 问题),占 4 字节。 同一条记录,不同时区的连接读到的 TIMESTAMP 值是不一样的,而 DATETIME 完全一致。 跨国业务建议统一「DATETIME 存 UTC 时间 + 应用层转换」或直接用 BIGINT 存毫秒时间戳。

4. SQLite 的动态类型与类型亲和性

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 强类型表,新项目建议开启。

5. Oracle DATE 带时分秒,其他库的 DATE 不带

Oracle 的 DATE 精确到,而 MySQL / PostgreSQL / SQL Server 的 DATE 只有年月日。 从 Oracle 迁出时如果无脑映射成 DATE,所有时间部分会被清零,订单时间、日志时间全部退化成 00:00:00, 而且这种丢失不会有任何报错。正确映射目标是 DATETIME / TIMESTAMP / DATETIME2

6. 空字符串与 NULL:Oracle 的独特行为

Oracle 把空字符串 '' 视为 NULL,其他所有数据库都把它当成长度为 0 的有效字符串。 因此 Oracle 里 WHERE col = '' 永远查不到任何行,必须写 WHERE col IS NULL。 迁移时带 NOT NULL 约束的列如果原本存的是空串,插入 Oracle 会直接违反约束。

7. 标识符大小写与引号规则

PostgreSQL 会把未加引号的标识符全部转成小写,Oracle 则全部转成大写, MySQL 在 Linux 上表名区分大小写、在 Windows 上不区分。 从 MySQL 的 userName 迁到 PG 后会变成 username,程序里写 "userName" 加引号查询就会报「列不存在」。 结论:库表字段一律用小写加下划线命名,这是跨库最安全的约定。

8. 自增主键的实现差异

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

9. MySQL utf8 不是真正的 UTF-8

MySQL 的 utf8 字符集最多只用 3 字节,无法存储 emoji 和部分生僻汉字(它们需要 4 字节)。 必须使用 utf8mb4。注意 utf8mb4 下 VARCHAR(255) 最多占 1020 字节, 而 InnoDB 单个索引键默认上限是 3072 字节,给长 VARCHAR 建联合索引时很容易超限。

10. SQL Server 的 TIMESTAMP 不是时间

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,用影响行数判断是否冲突。

💡 建表时的 5 条通用原则

🚀 你可能也用得上

💡 这个位置等你来 — 软广告位 / 友情链接 / 合作开发 正在开放中 查看价格

📘 使用说明

  1. 在搜索框输入类型名(如 varchar)实时过滤表格
  2. 点击数据库标签切换,选中两个库做左右对照高亮
  3. 查看每种类型的取值范围、占用字节与使用建议
  4. 参考底部的迁移坑位说明与类型选择决策指引

❓ 常见问题

Q: 存金额为什么不能用 FLOAT 或 DOUBLE?
A: 因为浮点数在二进制中无法精确表示大多数十进制小数,会产生舍入误差。经典例子:0.1 + 0.2 在 FLOAT 中不等于 0.3。金额计算涉及大量累加,误差会不断积累,最终可能出现对不上账的情况。正确做法是用 DECIMAL/NUMERIC 定点数,它以字符串方式精确存储每一位数字。MySQL 写作 DECIMAL(19,4)(总 19 位,小数 4 位),PostgreSQL 写 NUMERIC(19,4),Oracle 写 NUMBER(19,4)。另一种方案是用 BIGINT 存分(把元乘以 100),性能更好但需要应用层做单位换算。
Q: VARCHAR(255) 里的 255 是字节还是字符?
A: 取决于数据库。MySQL 5.0.3 之后 VARCHAR(n) 中的 n 是字符数而非字节数,一个 utf8mb4 汉字占 4 字节但只算 1 个字符。PostgreSQL 的 VARCHAR(n) 也是字符数。Oracle 默认是字节数(VARCHAR2(255) 只能存 85 个汉字),需要显式写 VARCHAR2(255 CHAR) 才按字符计。SQL Server 的 VARCHAR 是字节数、NVARCHAR 是字符数(每字符 2 字节)。这是跨库迁移最常见的坑之一,汉字数据从 MySQL 迁到 Oracle 经常报「值太大」。
Q: MySQL 的 DATETIME 和 TIMESTAMP 该用哪个?
A: TIMESTAMP 占 4 字节,范围 1970-2038(存在 2038 年问题),存储时会转成 UTC,读取时按会话时区转回,适合记录「事件发生的绝对时刻」如创建时间、更新时间。DATETIME 占 8 字节,范围 1000-9999 年,不做时区转换,存什么读什么,适合记录「墙上时钟时间」如生日、预约时间、合同到期日。跨时区业务推荐 TIMESTAMP 或者统一用 DATETIME 存 UTC 时间在应用层转换。PostgreSQL 对应的是 TIMESTAMPTZ 和 TIMESTAMP。
Q: SQLite 的动态类型是怎么回事?
A: SQLite 采用「类型亲和性」(Type Affinity)机制,列的类型声明只是一个建议而非强制约束。你在声明为 INTEGER 的列里插入字符串 'hello',SQLite 会照单全收。它只有 5 种存储类:NULL、INTEGER、REAL、TEXT、BLOB,任何声明类型都会按规则映射到其中之一(含 INT 的映射到 INTEGER,含 CHAR/CLOB/TEXT 的映射到 TEXT,等等)。这个特性让 SQLite 灵活,但也意味着从 SQLite 迁移到其他数据库时,可能会发现数据里混杂了不符合类型的脏值。SQLite 3.37 起提供 STRICT 表来启用严格类型检查。
👉 下一步 🏆 看看周榜热门 🧩 工作流中心 🔍 全部工具分类