根据外国的最后一个条目插入记录 table

Insert records based on last entry of foreign table

我有以下简单的 table 关系:

CREATE TABLE PRODUCT (
  ID                BIGSERIAL,
  TYPE              VARCHAR(24),
  TITLE             VARCHAR(128),
  PRIMARY KEY (ID)
);

CREATE TABLE EVENT (
  ID               BIGSERIAL,
  STATE            VARCHAR(64) NOT NULL,
  DATETIME         TIMESTAMP   NOT NULL,
  PRODUCT_ID        BIGINT,
  FOREIGN KEY (PRODUCT_ID) REFERENCES PRODUCT(ID),
  PRIMARY KEY (ID)
);

Event中的"STATE",是由Java枚举转换而来,可以是:INITIALIZED、AVAILABLE、PROCESSED等

我想执行以下操作:如果产品的最后一个事件条目具有状态 'PROCESSED',则在具有状态 'AVAILABLE' 的事件 table 中创建一个新插入。这必须适用于所有产品。

如您所见,事件 table 引用了产品。我看过:Insert, on duplicate update in PostgreSQL?

但是想不通

INSERT INTO EVENT( STATE, DATETIME, PRODUCT_ID )
SELECT 'AVAILABLE', current_timestamp, product_id
FROM EVENT e
WHERE PRODUCT_ID = 123
  AND e.STATE = 'PROCESSED'
  AND NOT EXISTS (
     SELECT 'anything' FROM event e1
     WHERE e1.PRODUCT_ID = e.PRODUCT_ID
       AND e1.DATETIME > e.DATETIME
);

如果这条命令要在multithread/multiuser环境下运行,那么整个交易必须由这四个命令组成,否则可能会出现重复记录:

BEGIN TRANSACTION;

/* lock the parent record */
PERFORM id FROM PRODUCT WHERE id = 123 FOR UPDATE;

INSERT INTO EVENT( STATE, DATETIME, PRODUCT_ID )
    SELECT 'AVAILABLE', current_timestamp, product_id
    FROM EVENT e
    WHERE PRODUCT_ID = 123
      AND e.STATE = 'PROCESSED'
      AND NOT EXISTS (
         SELECT 'anything' FROM event e1
         WHERE e1.PRODUCT_ID = e.PRODUCT_ID
           AND e1.DATETIME > e.DATETIME
    );

COMMIT;

如果您的数据是按顺序插入的,我更倾向于使用 id 进行比较,如:

insert into event (state, datetime, product_id)
    select 'AVAILABLE', current_timestamp, product_id
    from event e
    where e.state = 'PROCESSED' and
          not exists (select e2.state
                      from event e2
                      where e2.product_id = e.product_id and
                            e2.id > e.id  -- you can use timestamp
                     );

我更感兴趣的是你说state是一个enum。 Postgres 支持 enumerated types,而您没有。然而,你想做的事情通常使用检查约束来处理:

CREATE TABLE EVENT (
  ID               BIGSERIAL,
  STATE            VARCHAR(64) NOT NULL,
  DATETIME         TIMESTAMP   NOT NULL,
  PRODUCT_ID        BIGINT,
  FOREIGN KEY (PRODUCT_ID) REFERENCES PRODUCT(ID),
  PRIMARY KEY (ID),
  CONSTRAINT CHECK_EVENT_STATE CHECK (STATE IN ('INITIALIZED', 'AVAILABLE', 'PROCESSED'))
);