一般来说,在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)

因此,在处理这两种类型时,我们需要谨慎。要明确正在处理的类型是哪个,如果需要转换,转换后的类型是哪一个,还要注意类型转换后数据是否正确,以此保证程序的正确性。