0

我需要基于特定列创建自定义序列,作为前缀添加。我知道可以自定义序列以及 nextval,但我不确定是否可以使用特定表的列。

这是包含基本信息的表结构:

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  |
|--------|----------|---------|

注意id列和{sequence}元素之间没有直接关系

4

2 回答 2

1

对于以下测试结构:

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);
于 2019-09-11T15:14:49.760 回答
0

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

工作流程

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

表结构

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 |
----------------------
于 2019-09-12T11:04:57.507 回答