字符类型
PostgreSQL 的常用字符类型有 text、varchar(n)(或 character varying(n))和 char(n)(或 character(n))。它们都保存文本,主要差异在长度约束和空白填充,而不是“能不能存中文”。
三种类型对比
| 类型 | 长度规则 | 尾部空格 | 典型用途 |
|---|---|---|---|
text | 不声明最大长度 | 原样保存 | 说明、文章、可变长度名称 |
varchar(n) | 最多 n 个字符 | 不自动填充 | 有明确业务上限的文本 |
char(n) | 固定长度语义 | 写入时补空格,比较时通常忽略 | 少数固定宽度编码 |
在 PostgreSQL 中,text 和不带长度的 varchar 在存储和性能上通常没有实际差异。长度约束只有在业务规则需要由数据库强制执行时才有意义。
字符数、字节数和编码
varchar(n) 的 n 表示字符数,不是字节数。数据库编码通常为 UTF-8,一个中文字符可能占多个字节,但仍只计一个字符:
SELECT length('数据库'), octet_length('数据库');
length 返回字符数,octet_length 返回字节数。应用层还可能按 grapheme cluster(用户感知的字符)计数;emoji 和组合字符的显示长度不一定等于 PostgreSQL 的字符数。
长度限制与空值
CREATE TABLE users (
user_id bigint PRIMARY KEY,
display_name varchar(80) NOT NULL,
bio text,
CHECK (length(trim(display_name)) > 0)
);
varchar(80) 超出长度时会报错(显式转换和赋值的细节应按目标版本测试),而 text 不提供长度上限。长度限制不能替代内容校验:邮箱格式、用户名保留字和 Unicode 规范化仍需单独处理。
NULL 表示未知或缺失,'' 是长度为零的字符串。NOT NULL 只禁止 NULL,不会禁止空字符串;需要禁止空白文本时使用 CHECK。
char(n) 的陷阱
SELECT length('A '::char(3));
SELECT 'A'::char(3) = 'A '::char(3);
char(n) 会补齐空格,取值或连接时又常被去除尾部空格,容易造成展示和比较上的意外。固定宽度协议字段通常可用 char(n),但国家代码、货币代码等更常用 char(2)/char(3) 配合约束或字典表;普通名称不要使用它。
排序、大小写和区域设置
字符比较和排序受数据库排序规则(collation)影响。大小写不敏感不是 text 的内建属性,可以使用 lower 生成索引或 citext 扩展:
CREATE INDEX users_email_lower_idx ON users (lower(email));
SELECT * FROM users
WHERE lower(email) = lower($1);
排序规则升级可能改变索引顺序,跨环境迁移前应确认 locale 和 ICU 版本。不要用 lower 猜测所有语言的大小写规则。
模式匹配与索引
LIKE 'prefix%' 在合适的排序规则和索引配置下可能使用 B-tree;包含任意位置通配符('%part%')通常需要 trigram 等专用索引:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX users_name_trgm_idx ON users USING gin (display_name gin_trgm_ops);
索引表达式必须与查询表达式一致。对用户输入拼接 % 时,应同时处理转义,防止把用户输入误当作通配符。
文本类型选择
- 没有可靠上限时用
text,把业务校验放在CHECK或应用层。 - 有明确且稳定的最大字符数时用
varchar(n)。 - 只有协议或遗留格式确实要求定长时使用
char(n)。 - 不要为了性能把长文本强行改成
varchar(n);两者性能差异通常来自索引、查询和 TOAST,而非类型名字。