Greenplum数据增量导入的唯一值自增处理

    xiaoxiao2024-04-21  13

    阿里云的Greenplum(以下简称GP)已经公测了一段时间,陆续接到很多用户的反馈。其中一些使用上的问题比较有趣,在这里与大家分享一下。

    其中一个case是字段的唯一键和自增值问题。在导入GP之前,某id字段已经保证了唯一性,但在此次导入之后,可能会有更多的导入,这个时候希望GP在原来最大id值的基础上自增。

    GP是在PostgreSQL(以下简称PG)上开发而来,其操作基本沿用。在PG上,实现自增的方法是通过serial:

    postgres=> create table tuniq(id serial, name text); CREATE TABLE postgres=> insert into tuniq (name) values('zero'); INSERT 0 1 postgres=> insert into tuniq (name) values('second'); INSERT 0 1 postgres=> postgres=> postgres=> select * from tuniq; id | name ----+-------- 1 | zero 2 | second (2 rows)

    这里的serial,并不是一个数据类型,而是通过建立一个全局序列“tuniq_id_seq”(表名_字段名_seq)实现的,每次插入的时候会从这个seq中取值作为字段的默认值,从而做到自增。

    那么,如果你执行下面的语句会怎么样?

    postgres=> insert into tuniq (id, name) values(1, 'second');

    在id没有唯一约束的情况下,这是可以执行成功的。原因是id字段并没有加任何约束,而serial只是简单的从sequence给id赋值而已。这样就带来一个问题:

    postgres=> select * from tuniq; id | name ----+-------- 1 | zero 2 | second 1 | second (3 rows)

    如果在这个字段上有唯一约束的话,那么开始的时候导入包括id在内的数据,之后执行不包括id的插入的时候,就会去从sequence取值。而这个时候,因为sequence的当前最新值尚未更新,所以可能会出现与已导入数据冲突的情况,如:

    postgres=> create table tuniq(id serial unique, name text); CREATE TABLE postgres=> postgres=> postgres=> insert into tuniq values(0, 'zero'); INSERT 0 1 postgres=> insert into tuniq values(1, 'first'); INSERT 0 1 postgres=> select * from tuniq; id | name ----+------- 0 | zero 1 | first (2 rows) postgres=> insert into tuniq (name) values('second'); ERROR: duplicate key value violates unique constraint "tuniq_id_key" DETAIL: Key (id)=(1) already exists.

    这个问题的解决方法也很简单:

    postgres=> select setval('tuniq_id_seq', max(id)) from tuniq; setval -------- 1 (1 row) postgres=> insert into tuniq (name) values('second'); INSERT 0 1 postgres=> select * from tuniq; id | name ----+-------- 0 | zero 1 | first 2 | second (3 rows)

    更详细的用法和解释参考这里。

    那如果是开始加了唯一键约束,但没有采用serial该如何实现字段的继续递增?

    正所谓:知其然,知其所以然。serial的原理,不过是从sequence取值作为字段的默认值而已。那如果想要做到类似的方式,用同样的方式做就好了。

    让我们模拟一下这个场景:

    postgres=> create table tuniq(id int unique, name text); NOTICE: CREATE TABLE / UNIQUE will create implicit index "tuniq_id_key" for table "tuniq" CREATE TABLE postgres=> postgres=> insert into tuniq(id, name) values(0, 'zero'); INSERT 0 1 postgres=> insert into tuniq(id, name) values(1, 'first'); INSERT 0 1 postgres=> insert into tuniq(id, name) values(2, 'second'); INSERT 0 1 postgres=> postgres=> select * from tuniq ; id | name ----+-------- 1 | first 0 | zero 2 | second (3 rows)

    这个数据已经导入完成,后续想让id键自增的话,需要先建一个sequence,并更新到最新的值:

    postgres=> create sequence tuniq_id_seq; CREATE SEQUENCE postgres=> postgres=> select setval('tuniq_id_seq', max(id)) from tuniq; setval -------- 2 (1 row) postgres=> postgres=> ALTER TABLE tuniq ALTER id set default nextval('tuniq_id_seq'::regclass); ALTER TABLE

    这个时候,就可以把sequence的值作为tuniq表id字段的默认值了,如:

    postgres=> insert into tuniq(name) values('third'); INSERT 0 1 postgres=> postgres=> postgres=> select * from tuniq; id | name ----+-------- 1 | first 3 | third 0 | zero 2 | second (4 rows)

    从以上我们可以看出,用PG/GP的自增需要注意:

    serial是通过sequence设置字段的默认值 可以考虑加上唯一约束,防止主动插入该字段的值,破坏该字段值的自增序和唯一性(如果业务关心的话) 相关资源:敏捷开发V1.0.pptx
    最新回复(0)