PostgreSQL自带三种存储字符的数据类型。char,varchar和text。
我们经常看到的是,当客户想要存储字符时使用varchar(n),并限制允许存储到字段/列的字符的数量。
在我们进行更深入的细节前:你唯一想用char或者varchar的原因是,二者的定义都符合SQL标准。
至于其他的原因,你应该用text。
让我们从三张示例表开始,表中包含一个char字段,一个varchar字段和text字段:
postgres=# create table t1 ( a char(2000) );
CREATE TABLE
postgres=# \d t1
Table "public.t1"
Column | Type | Collation | Nullable | Default
--------+-----------------+-----------+----------+---------
a | character(2000) | | |
postgres=# create table t2 ( a varchar(2000) );
CREATE TABLE
postgres=# \d t2
Table "public.t2"
Column | Type | Collation | Nullable | Default
--------+-------------------------+-----------+----------+---------
a | character varying(2000) | | |
postgres=# create table t3 ( a text );
CREATE TABLE
postgres=# \d t3
Table "public.t3"
Column | Type | Collation | Nullable | Default
--------+------+-----------+----------+---------
a | text | | |
postgres=#
表t1和t2可以存储字符串的上限是长度2000个的字符(非字节)。表t3可以存储任意字符串上限至1GB。我们将添加1000000行来看看这些数据的第一个不同点,如下:
postgres=# \timing on
Timing is on.
postgres=# insert into t1 select lpad(i::char,100,'0') from generate_series(1,1000000) i;
INSERT 0 1000000
Time: 42041.031 ms (00:42.041)
postgres=# insert into t2 select lpad(i::varchar,100,'0') from generate_series(1,1000000) i;
INSERT 0 1000000
Time: 2813.176 ms (00:02.813)
postgres=# insert into t3 select lpad(i::text,100,'0') from generate_series(1,1000000) i;
INSERT 0 1000000
Time: 2790.226 ms (00:02.790)
postgres=#
你会注意到,插入数据进包含char字段的表t1慢了很多。在varchar(n)和text表之间并没有太多差别。问题是:为什么char(n)表比其他两个表慢如此多呢?如果你比对两个表的大小,原因非常明显。
postgres=# select pg_size_pretty(pg_relation_size('t1'));
pg_size_pretty
----------------
1953 MB
(1 row)
Time: 1.719 ms
postgres=# select pg_size_pretty(pg_relation_size('t2'));
pg_size_pretty
----------------
128 MB
(1 row)
Time: 0.682 ms
postgres=# select pg_size_pretty(pg_relation_size('t3'));
pg_size_pretty
----------------
128 MB
(1 row)
Time: 0.629 ms
第一个包含char(n)字段的表差不多2GB,而其他两个大小只有128MB,这是非常大的一个差别。原因是,char(n)以空白补齐并且总是消耗2000个字符大小,而varchar(n)和text是动态的。动态意味着它们只消耗存储字符串所需的空间,而不是尽可能多的空间。
这已经取消了char(n)的资格。
varchar(n)和text呢?二者在磁盘上的space消耗并没有不同。两者都用了需求所需的space去存储字符串。
那不同的是什么呢?不同的当然是varchar(n)类型的长度约束。你不能插入超过2000个字符到表t2中:
postgres=# insert into t2 select lpad('i',2000,'0');
INSERT 0 1
Time: 9.201 ms
postgres=# insert into t2 select lpad('i',2001,'0');
ERROR: value too long for type character varying(2000)
Time: 3.445 ms
存储text字段的表t3就没有如此的限制:
postgres=# insert into t3 select lpad('i',2001,'0');
INSERT 0 1
Time: 9.818 ms
这也许似乎是varchar数据类型的一个优势,但你可以简单地通过添加一个约束为text使用相同行为:
postgres=# delete from t3 where a = lpad('i',2001,'0');
DELETE 1
Time: 200.586 ms
postgres=# alter table t3 add constraint c1 check ( length(a) < 2001 );
ALTER TABLE
Time: 289.121 ms
postgres=# insert into t3 select lpad('i',2001,'0');
ERROR: new row for relation "t3" violates check constraint "c1"
DETAIL: Failing row contains (0000000000000000000000000000000000000000000000000000000000000000...).
Time: 1.623 ms
当数据插入时,那有什么不同吗?
postgres=# truncate t2,t3;
TRUNCATE TABLE
Time: 58.063 ms
postgres=# insert into t2 select lpad(i::varchar,100,'0') from generate_series(1,1000000) i;
INSERT 0 1000000
Time: 2281.157 ms (00:02.281)
postgres=# insert into t3 select lpad(i::text,100,'0') from generate_series(1,1000000) i;
INSERT 0 1000000
Time: 2572.467 ms (00:02.572)
并不见得。那varchar(n)如此的原因是什么呢?服从SQL标准罢了。
原文标题:Do you really need varchar(n) in PostgreSQL?
原文作者:Daniel Westermann
原文链接:https://www.dbi-services.com/blog/do-you-really-need-varcharn-in-postgresql/




