一般来说,在PostgreSQL中,char 类型是表示固定长度的字符串的类型。例如, char(n) 用来表示长度为N个字符的字符串。不过,PostgreSQL 自身的一些细小缺陷,使得上面的论断事实上并不是很严谨。何以见得?下面的这个探究char类型实验,会揭开问题的面纱。
我们通过下面的SQL语句创建一张表:
create table tb_char
(
quoted_char_col "char",
char_col char,
char1_col char(1),
char2_col char(2)
);
可以看到表 tb_char 有四列,他们的类型依次是带引号的 "char",不带引号且未指定长度的char,char(1),char(2)。char(1)和char(2) 的含义明确,而另外两种类型是什么含义呢?
我们执行下列查询,从pg_attribute中查看这几列的属性:
select attrelid,attname,atttypid,attlen,atttypmod from pg_attribute where attrelid = 'tb_char'::regclass::oid and attnum > 0
查询结果如下:
attrelid | attname | atttypid | attlen | atttypmod
----------+-----------------+----------+--------+-----------
391725 | quoted_char_col | 18 | 1 | -1
391725 | char_col | 1042 | -1 | 5
391725 | char1_col | 1042 | -1 | 5
391725 | char2_col | 1042 | -1 | 6
(4 rows)
从查询结果中可以看出:
char_col 的类型char1_col 完全相同。因此,在创建表时,不带引号且未指定长度
的char会被默认视为char(1)。
而 quoted_char_col 的类型 "char" 有哪些性质呢?
我们通过如下查询,从类型表pg_type中查看 oid 为 18和1042的类型:
select oid,typname,typlen from pg_type where oid in (18,1042);
查询结果如下:
oid | typname | typlen
------+---------+--------
18 | char | 1
1042 | bpchar | -1
(2 rows)
嗯? 在pg_type中,oid = 18 的类型的名称是char,而它是建表语句中quoted_char_col字段的类型,带引号的"char";而 oid =1042的类型的名称是bpchar,而它是建表语句中char_col字段的类型,不带引号的char。这同样的类型,在系统表pg_type 中和建表语句中的名称竟然是不一致的。
我们再看查询中的typlen的值, pg_type中typname为char 的类型,typlen = 1;而pg_type中typname为bpchar 的类型,typlen = -1。
PostgreSQL 官方文档对这个字段的描述是:
对于一个固定尺寸的类型,typlen是该类型内部表示的字节数。对于一个变长类型,typlen为负值。-1表示一个"varlena"类型(具有长度字),-2表示一个以空值结尾的C字符串。
也就是说,表 tb_char中quoted_char_col字段的类型,即pg_type 中的char,建表语句中带引号的”char”,长度是1个字节;而tb_char中char_col,char1_col字段的类型,也就是在pg_type 中的bpchar,建表语句中不带引号的char,长度则是取决于用户设定。
那么pg_type 中的char类型通常用在哪里呢?
这个类型在实际项目中一般不会使用,也不推荐使用。它只在PostgreSQL 的系统表中使用。如pg_class. relkind,. pg_constraint.contype, pg_aggregate. aggkind等。
另外,pg_type 中的char类型还有一个特点,那就是当你为具有这个类型的字段赋值时,若这个值的长度大于1个字符,程序会自动将其截取为1个字符。而对类型是pg_type 中的bpchar类型,即建表语句中不带引号的char 的字段做同样的操作,则会报错。
执行下面的插入语句:
insert into tb_char (quoted_char_col,char_col,char1_col,char2_col) values ('aa','a','a','aa');
执行下面的查询语句:
select * from tb_char;
查看插入结果:
quoted_char_col | char_col | char1_col | char2_col
-----------------+----------+-----------+-----------
a | a | a | aa
可以看出, 程序自动将向quoted_char_col字段插入的值截取为了“a”。
执行下面的插入语句:
insert into tb_char (quoted_char_col,char_col,char1_col,char2_col) values ('a','aa','a','aa');
则会报错:
ERROR: value too long for type character(1)
因此,在处理这两种类型时,我们需要谨慎。要明确正在处理的类型是哪个,如果需要转换,转换后的类型是哪一个,还要注意类型转换后数据是否正确,以此保证程序的正确性。