字符类型
本教程共 50 篇 · 第 12 篇 · 更新于 2026-07-31 · 约 7 分钟阅读
12. 字符类型
本节目标:学完能分清 char、varchar、text,知道日常该用哪个存字符串。
存文字要用字符类型。PostgreSQL 主要提供三种:char、varchar、text。名字像,差别却不小。我刚学的时候也以为 char 更快、更省事,结果被定长补空格坑过一次。先把三者区别讲透,再告诉你怎么选。
三种类型一览
| 类型 | 写法 | 特点 |
|---|---|---|
| 定长 | char(n) / character(n) | 固定长度,不足补空格 |
| 变长有上限 | varchar(n) / character varying(n) | 最长 n 个字符,不补空格 |
| 变长无限 | text / varchar(不带 n) | 任意长度 |
char(n) 是定长。存入比 n 短的字符串,它会在右边补空格凑够长度;查询显示时也带这些空格。比如存 'ok' 到 char(4),实际存的是 'ok '。
varchar(n) 是变长,只限制「最长 n 个字符」。超出长度直接报错,这是它有别于 text 的关键。
text 不限长度,和「不带长度参数的 varchar」基本是一回事。
实际看看区别
CREATE TABLE demo (
a CHAR(4),
b VARCHAR(10),
c TEXT
);
INSERT INTO demo VALUES ('ok', 'hello', '这是一段很长的文本,text 照单全收');
往 a 塞超过 4 个字符会报错:
INSERT INTO demo (a) VALUES ('hello');
-- ERROR: value too long for type character(4)
varchar(10) 超过 10 个字符同样报错。text 则随便塞,没有上限烦恼(当然磁盘还是会满)。
TipPostgreSQL 里这三种类型性能几乎没差别。不像某些数据库,
char(n)并不会更快,反而定长补空格常常带来麻烦。大多数情况用text或varchar(n)即可。
补空格的坑
char(n) 补的空格会在比较时「作祟」。下面两种写法结果可能和你预期不一样:
SELECT 'ok'::char(4) = 'ok' AS eq1; -- true,比较时忽略尾部空格
SELECT 'ok'::char(4) = 'ok ' AS eq2; -- true,尾部空格被忽略
但你如果和 text 拼接或按字节处理,空格就显现出来了。比如取长度:
SELECT length('ok'::char(4)); -- 4,因为补了两个空格
SELECT length('ok'::text); -- 2
所以除非你确实要定长语义(比如固定长度的国标编码),否则别用 char(n)。
长度相关的几个点
varchar(n) 的 n 是「字符数」不是「字节数」。一个中文汉字算 1 个字符,但可能占 3 个字节。所以 varchar(10) 能存 10 个汉字,也能存 10 个英文字母。
想看字符串长度用 length,想看字节数用 octet_length:
SELECT length('你好'), octet_length('你好'); -- 2, 6(UTF-8 下每个汉字 3 字节)
排序和比较还受「排序规则(collation)」影响,比如是否区分大小写、是否按拼音。一般沿用库默认即可,特殊需求可指定。下面用 "C"(按字节序,任何系统都内置)演示;真实的中文排序规则名取决于操作系统装了哪些 locale,常见如 zh_CN.utf8,可用 \dC 查看本机有哪些:
CREATE TABLE t (name VARCHAR(50) COLLATE "C");
该用哪个
- 需要数据库帮你「拦住超长字符串」→ 用
varchar(n),比如用户名、邮箱。这样超长输入会直接报错,由应用层捕获。 - 长度完全自由 → 用
text,比如备注、文章内容、日志。 - 几乎不需要用
char(n)。除非你确实想要定长补空格的语义,否则它只会白白占空间、还可能引入空格比较的怪问题。
拿我们的 users 表举例:
CREATE TABLE users (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
username VARCHAR(50) NOT NULL, -- 限制长度
email VARCHAR(120),
age SMALLINT,
created_at TIMESTAMPTZ DEFAULT now()
);
Note
varchar不带长度时和text等价;char不带长度时等价于char(1),这点容易踩坑,写的时候别省那个 n。想写变长就显式写text,意图更清楚。
Tip
char(n)还有个隐蔽坑:比较时会忽略尾部空格。比如某列是char(4),存了'AB ',你写WHERE code = 'AB'居然能匹配上,因为末尾空格被无视了。这会让「按精确值更新」悄悄改到不该改的行。我之前就踩过:用char(2)存省份简称,结果'京 '和'京'被判相等,统计时把两条并成一条。所以除非明确要定长语义,否则一律用varchar或text。
到底该听谁的
新手常纠结「varchar 和 text 到底选哪个」,结论其实很轻松:两者底层一样,按「要不要限制长度」来选就行。varchar(n) 的价值是那道长度防线——超过 n 就报错,应用层直接拿到异常,不会把超长脏数据写进库。而 text 把长度完全交给你自己控制,适合留言、简介这类没上限的内容。
所以别再为性能纠结,它们没差别。也别为了「省事」全用 text 而丢了 varchar(n) 的约束价值。该限长的限长,不该限长的放开,这就是全部取舍。
排序规则与比较
字符串怎么排、怎么比,由「排序规则(collation)」决定。默认沿用数据库创建时的设置。它影响 ORDER BY 的结果,也影响大小写是否敏感。
CREATE TABLE t (name VARCHAR(50) COLLATE "C");
如果你希望某列不区分大小写比较,可以指定对应 collation,或在查询里转小写后再比。
存储与性能的真相
很多人担心 text 比 varchar(n) 慢,其实在 PostgreSQL 里两者底层存储几乎一样,都是变长结构。区别只在 varchar(n) 多了一道「长度上限」检查。真正影响性能的是索引设计、数据量和查询写法,不是选 text 还是 varchar。
Note超长的
text(几 MB 的文章)会触发 TOAST 机制,超出阈值的内容被压缩存到表外,对主表行影响不大。所以存长文本用text没心理负担。
顺手记几个通用字符串函数:length() 取字符数、substring() 截取、upper()/lower() 大小写、trim() 去空格、position() 找位置,对任意字符类型都适用。
长度参数的意义
varchar(n) 的 n 是「约束」不是「性能优化」。它存在的价值是帮你挡住不合规格的数据,而不是让存储变快。所以:
- 有明确业务上限(手机号 11 位、用户名 50 字符)→ 写上 n,当一道防线。
- 长度完全不定(文章、评论)→ 直接用
text,别硬编一个大 n。
总结一句:日常优先 text 和 varchar(n),把 char(n) 留在真正需要定长的少数场景。