PostgreSQL の text は、長さの制限が無い文字列型です。MySQL の TEXT 型のように 65,535 バイトといった上限はありません。この記事の出力は PostgreSQL 17.6 で採りました。
まず見る(結論)
textとvarchar(n)は、長さの検査があるかどうかだけが違う。性能は同じ- 格納できる最大は約 1GB(公式ドキュメント)
varchar(n)のnは文字数。日本語も1文字は1と数えるchar(n)は空白で埋められ、比較や連結で空白が消える。特別な理由が無ければ使わない
3つの型の違い
公式ドキュメントには、3つの型の間に性能差は無く、空白埋めのある char(n) はむしろ少し遅いと書かれています。
表 t_txt(t text, v varchar(5), c char(5)) で確かめました。
varchar(n) を超えるとエラー、n は文字数
INSERT INTO t_txt(v) VALUES ('abcdef');
ERROR: value too long for type character varying(5)
INSERT INTO t_txt(v) VALUES ('abcde'); -- 通る
INSERT INTO t_txt(v) VALUES ('あいうえお'); -- 通る
SELECT v, length(v), octet_length(v) FROM t_txt;
v | length | octet_length
------------+--------+--------------
abcde | 5 | 5
あいうえお | 5 | 15
length が文字数、octet_length がバイト数です。
エラーにならず切り詰められる場合が2つある
超えた分が空白だけなら、エラーにならず、空白が削られて入ります。'abc '(6文字)を入れると、'abc '(5文字)になりました。
明示的にキャストしたときも、エラーではなく切り捨てです。
SELECT 'abcdefg'::varchar(5) AS cast_result;
cast_result
-------------
abcde
アプリケーション側でキャストしてから入れている処理があると、データが欠けても気づけません。
char(n) は空白で埋められる
INSERT INTO t_txt(c) VALUES ('ab');
SELECT c, length(c), octet_length(c), c = 'ab' AS eq_ab, c || '|' AS concat_bar FROM t_txt;
c | length | octet_length | eq_ab | concat_bar
-------+--------+--------------+-------+------------
ab | 2 | 5 | t | ab|
末尾の空白に意味があるデータでは、入れた値と取り出した値が一致しません。
長い文字列と保存サイズ
text の列に 10万文字の repeat('x', 100000) を入れると、length は 100000、pg_column_size は 1156 バイトでした。一定の大きさを超えた値は自動で圧縮され、さらに大きければ表の外(TOAST)に置かれます。同じ文字の繰り返しなので、ここまで小さくなっています。
短い値のサイズは text と varchar(n) で同じです。
SELECT pg_column_size('abc'::text) AS size_text,
pg_column_size('abc'::varchar(10)) AS size_varchar10,
pg_column_size('abc'::char(10)) AS size_char10;
size_text | size_varchar10 | size_char10
-----------+----------------+-------------
7 | 7 | 14
char(10) は空白で10文字に埋めるので、その分大きくなります。
型を変える
varchar(n) から text へは、そのまま変えられます。
ALTER TABLE t_txt ALTER COLUMN v TYPE text;
ALTER TABLE
短くする方向は、入っている値が収まらなければ失敗します。
ALTER TABLE t_txt ALTER COLUMN v TYPE varchar(3);
ERROR: value too long for type character varying(3)
上限を決めにくい列は text にしておき、長さを制限したくなったら CHECK (char_length(列) <= 100) の制約を足す方法もあります。制約なら、型を変えずに付け外しできます。