以列为前缀的自定义渐进序列(每年)

Custom progressive sequence (per year) with a column as prefix

我需要根据特定列创建自定义序列,添加为前缀。我知道可以自定义序列以及 nextval,但我不确定是否可以使用特定 table.

的列

这是 table 的结构,其中包含基本信息:

create table tab
(
    id serial not null
        constraint tab_pkey primary key,
    year varchar(4) not null,
    seq  varchar(20) not null
);

create sequence tab_id_seq as integer;

我想按照以下格式自动填充 "seq" 列,就像正常序列一样:

{year}_{sequence}

其中{year}_是前缀,而{sequence}是每年从1重新开始的累进。

DESIRED RESULT
|--------|----------|---------|
|   id   |   year   |   seq   |
|--------|----------|---------|
|   10   |   2019   | 2019_1  |
|--------|----------|---------|
|   11   |   2019   | 2019_2  |
|--------|----------|---------|
|   12   |   2019   | 2019_3  |
|--------|----------|---------|
|   13   |   2019   | 2019_4  |
|--------|----------|---------|
|   14   |   2020   | 2020_1  | <--- sequence restarting
|--------|----------|---------|
|   15   |   2020   | 2020_2  |
|--------|----------|---------|
|   16   |   2020   | 2020_3  |
|--------|----------|---------|

N.B。 id 列与 {sequence} 元素

之间没有直接关系

对于以下测试结构:

create table test 
(
    id serial primary key
    , year_val int
    , seq varchar (10)
);
create or replace function fn_test () returns trigger language plpgsql as $$
declare
    res_name varchar;
begin
    drop table if exists tmp_test;
    create temporary table tmp_test as select * from test;
    insert into tmp_test values (new.id, new.year_val);

    with cte as
    (
        select *
            , year_val::varchar||'_'||(count(*) over (partition by year_val order by id))::varchar as built_res_name
        from tmp_test
    )
    select built_res_name into res_name
    from cte
    where id = new.id;

    new.seq := res_name;
    return new;
end;
$$;

CREATE TRIGGER tg_test BEFORE INSERT ON test
    FOR EACH ROW EXECUTE FUNCTION fn_test();
insert into test (year_val)
values (2019),(2019),(2019),(2019),(2020),(2020),(2020);

最后我找到了一个解决方案,使用多个序列(每年一个),在输入记录时动态创建。 触发器,在插入之前调用创建序列(如果不存在)并将值分配给 seq 列(如果未分配)的过程。

工作流程

  • 记录插入
  • 序列创建'tab_ {year} _seq_id'如果不存在
  • 如果列 seq 为空,则分配值 nextval (tab_ {year} _seq_id)
  • 测试插入和删除以验证列是否以正确的方式填充

TABLE 结构

CREATE TABLE tab (
    id serial not null constraint tab_pkey primary key,
    year varchar(4) not null,
    seq  varchar(20)
);

函数

CREATE FUNCTION tab_sequence_trigger_function() RETURNS trigger AS $$
  BEGIN
    IF NEW.seq IS NULL OR NEW.seq = '''' THEN
      EXECUTE ('CREATE SEQUENCE IF NOT EXISTS tab_' || NEW.year || '_id_seq AS INTEGER');
      NEW.seq = NEW.year || '_' || nextval('tab_' || NEW.year || '_id_seq');
    END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

触发

CREATE TRIGGER tab_sequence_trigger
  BEFORE INSERT ON tab
  FOR EACH ROW
EXECUTE PROCEDURE tab_sequence_trigger_function();

测试

INSERT INTO tab (year) VALUES (2019);
INSERT INTO tab (year) VALUES (2019);
INSERT INTO tab (year) VALUES (2019);
INSERT INTO tab (year) VALUES (2019);
INSERT INTO tab (year) VALUES (2019);
DELETE FROM tab WHERE id=5;
INSERT INTO tab (year) VALUES (2019);
INSERT INTO tab (year) VALUES (2019);
INSERT INTO tab (year) VALUES (2020);
INSERT INTO tab (year) VALUES (2020);
INSERT INTO tab (year) VALUES (2021);
DELETE FROM tab WHERE id=8;
DELETE FROM tab WHERE id=9;
INSERT INTO tab (year) VALUES (2021);
INSERT INTO tab (year) VALUES (2020);

结果

SELECT * FROM tab;

----------------------
| id | year |  seq   |
----------------------
|  1 | 2019 | 2019_1 |
----------------------
|  2 | 2019 | 2019_2 |
----------------------
|  3 | 2019 | 2019_3 |
----------------------
|  4 | 2019 | 2019_4 |
----------------------
|  6 | 2019 | 2019_6 |
----------------------
|  7 | 2019 | 2019_7 |
----------------------
| 10 | 2021 | 2021_3 |
----------------------
| 11 | 2021 | 2021_4 |
----------------------
| 12 | 2020 | 2020_3 |
----------------------