浅探PostgreSQL的 char 类型

    xiaoxiao2022-07-13  165

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

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

    最新回复(0)