关于主键:自动增加,取决于PostgreSQL中列的值

Auto increment depending on value of column in PostgreSQL

接下来的问题是,我是否可以根据特定列(此处为business_uuid)的值自动递增(verification_number),以便使该verify_number根据该business_uuid自身的verification_number的最高数目递增一(1)?

数据库如下所示:
表:验证

1
2
3
verification_id = INTEGER, SEQUENCE (PRIMARY KEY)
business_uuid = text
verification_number = INTEGER

verification_id是此表中的主键,我希望verification_number遵循它自己的自动增量,这取决于仅针对business_uuid过滤的最大值。

business_uuid是每个企业的唯一标识符。

有可能吗?


我认为这是一个不好的设计,但是如果您真的想要这样做,可以按以下步骤实现:

  • 创建唯一约束:

    1
    2
    3
    ALTER TABLE verification
       ADD CONSTRAINT verification_uuid_nr_unique
          UNIQUE (business_uuid, verification_number);

    由此创建的索引还将使以下触发功能更快。

  • 创建一个BEFORE触发器以修改verification_number

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    CREATE OR REPLACE FUNCTION veritrig() RETURNS TRIGGER
       LANGUAGE plpgsql AS
    $$BEGIN
       SELECT COALESCE(MAX(verification_number)+1, 1) INTO NEW.verification_number
          FROM verification
          WHERE business_uuid = NEW.business_uuid;
       RETURN NEW;
    END;$$;

    CREATE TRIGGER veritrig
       BEFORE INSERT OR UPDATE ON verification FOR EACH ROW
       EXECUTE PROCEDURE veritrig();
  • 插入这样的新值:

    1
    INSERT INTO verification (business_uuid) VALUES ('42');

    然后将根据需要设置verification_number

  • 但是并发性问题。

    如果多个会话尝试同时插入或更新表,则会出现如下错误:

    1
    2
    ERROR:  duplicate KEY VALUE violates UNIQUE CONSTRAINT"verification_uuid_nr_unique"
    DETAIL:  KEY (business_uuid, verification_number)=(43, 1) already EXISTS.

    这是因为在并发修改中的SELECT语句将仅看到当前(已提交)表的内容,并且可能错误地尝试为business_uuid插入相同的verification_number

    没有任何方法可以避免锁定表。但是,如果收到这样的错误,则可以简单地重试该事务,并且很有可能在下次运行。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    CREATE OR REPLACE FUNCTION public.creaid(
        IN key_field text,
        IN table_mane text,
        OUT id INTEGER)
        RETURNS INTEGER AS
    $BODY$
        DECLARE  maxid INT;
        BEGIN
            EXECUTE 'SELECT max('||key_field||') FROM '||TABLE_NAME INTO maxid;
            IF maxid IS NULL THEN
                id = 1;
            ELSE
                id = maxid + 1;
            END IF;
        END;
    $BODY$
    LANGUAGE plpgsql VOLATILE
    COST 100;

    然后,您可以将该函数作为数据库每个表的每个键值的默认值进行调用,如下所示:

    1
    DEFAULT creaid('key_field'::text, 'table_name'::text)

    尚不清楚是否要将verification_number保存到表中,但可以在查询时创建它:

    1
    2
    3
    4
    5
    6
    SELECT verification_id, business_uuid,
        ROW_NUMBER() OVER(
            partition BY business_uuid
            ORDER BY verification_id
        ) AS verification_number
    FROM verification

    上述方法的问题是,如果删除行,verification_number将会更改

    窗口功能