在PostgreSQL中做数据转换时,很多人习惯直接用cast做类型强转,但一旦涉及千分位、货币符号、自定义日期样式这类需求,cast就无能为力了。PostgreSQL专门提供了一组数据类型格式化函数,包括to_char、to_date、to_number和to_timestamp,它们通过模板字符串精确控制输入输出的格式,是报表输出、异构数据导入、Oracle系统迁移等场景的利器。本文将系统讲解这组函数的用法、模板语法以及常见的误区。

一、格式化函数家族有哪些,各自负责什么
PostgreSQL的格式化函数一共有四个,功能上两两对应:to_char负责把数值或时间戳转成字符串,to_number负责把字符串解析成数值,to_date负责把字符串解析成日期,to_timestamp负责把字符串解析成时间戳。它们都接收两个参数:待转换的数据和格式模板。
to_char是最常用的一个。对数值类型,它可以控制小数位数、千分位、货币符号和负号位置;对时间类型,它可以输出几乎任意样式的日期时间文本,例如:
-- 数值格式化 SELECT to_char(1234567.891, 'FM999,999,990.00'); -- 结果:1,234,567.89 -- 时间格式化 SELECT to_char(now(), 'YYYY-MM-DD HH24:MI:SS'); -- 结果:2024-06-15 14:30:05 SELECT to_char(now(), 'YYYY"年"MM"月"DD"日"'); -- 结果:2024年06月15日
to_number和to_date则是反方向的解析。在处理从Excel或CSV导入的脏数据时,它们比cast安全得多,因为cast遇到带逗号的数字或非标准日期会直接报错,而格式化函数只要模板匹配就能正确解析:
SELECT to_number('1,234,567.89', '9G999G999D99'); -- 结果:1234567.89
SELECT to_date('2024/06/15', 'YYYY/MM/DD'); -- 结果:2024-06-15
SELECT to_timestamp('2024-06-15 14:30:05', 'YYYY-MM-DD HH24:MI:SS');
需要说明的是,模板中的9表示一个数字位,G代表分组分隔符(通常是逗号),D代表小数点,MI表示负号位置。这些模板修饰符是理解整组函数的关键。
二、格式模板的常用修饰符与使用技巧
时间格式模板是最丰富的部分。常用的有YYYY(四位年份)、YY(两位年份)、MM(月份)、DD(日)、HH24(24小时制)、HH12(12小时制)、MI(分钟)、SS(秒)、MS(毫秒)、US(微秒)、AM或PM(上下午标识)、Day(星期的完整英文名)以及Month(月份英文名)等。搭配引号可以嵌入固定文字,例如'YYYY"年第"Q"季度"'中的Q会输出季度数字。
数值模板方面,0和9的区别值得注意:9表示有数字才占位,0表示强制占位。比如to_char(42, '00099')输出00042,而to_char(42, '99999')输出42(前面留空)。这在需要对齐报表数字列时特别有用。另外还有L(本地货币符号,如人民币符号)、MI(负号放右侧或标出位置)、S(强制符号)、RN(罗马数字)、TH或th(序数后缀,如4TH)等修饰符。
SELECT to_char(42, 'FM00000'); -- 00042,强制补零 SELECT to_char(42, 'FM00th'); -- 42nd,序数后缀 SELECT to_char(1234.5, 'FML999G990.00'); -- 人民币符号加千分位 SELECT to_char(now(), 'Day, DD Month YYYY');
还有一个实用技巧:模板中两个连续的单引号表示一个字面单引号,而用双引号包裹的内容会被当作普通文本原样输出,不被解析为模板模式。掌握这两条规则,基本可以拼出任何想要的输出样式。
三、与Oracle的差异以及常见的踩坑点
很多从Oracle迁移过来的项目会大量使用这组函数,因为函数名和大部分模板语法都一致。但PostgreSQL在细节上与Oracle不同,这也是最容易踩坑的地方。
第一个坑是FM填充模式。to_char默认会在输出前补空格占位,比如to_char(42, '999')的结果是空格加42,占三个字符宽度。如果不想要这些填充空格,需要在模板前加FM前缀。而更隐蔽的是to_number方向:如果解析时模板里没有FM,带前导空格的字符串可能解析异常。建议在双向转换时保持模板风格一致。
第二个坑是to_date的宽容性带来的脏数据风险。PostgreSQL对某些不严格的输入会尝试容错,例如年份只给两位时按一定的规则补全,这在生产环境解析用户输入时可能产生意料之外的日期。更稳妥的做法是先校验格式再转换,或者用to_timestamp配合异常处理。
第三个坑是L、G、D这类修饰符受地区设置影响。L对应的货币符号、G对应的分组符会随服务器或会话的LC_NUMERIC设置变化。如果系统要输出固定格式(比如对接固定宽度的对账文件),不要依赖这些修饰符,改用字面量的逗号和句点更可靠:
-- 更可控的写法:不用G和D,直接写死分隔符
SELECT to_number('1,234,567.89', '9,999,999.99');
第四个坑是性能。格式化函数的解析成本高于直接cast,在大批量ETL场景中,如果源数据格式本身就是标准的ISO样式,直接用::numeric或::timestamp转换会快很多,只在格式不规则时才动用模板函数。
总结一下,这四个格式化函数的核心价值在于精确可控的双向转换:输出时用于报表和展示,输入时用于解析脏数据。使用时记住三条原则——模板修饰符与地区设置解耦、FM模式双向一致、大批量场景优先用cast,就能避开绝大多数问题。
PostgreSQL格式化函数to_char修改时间:2026-09-02 23:45:05