【问题标题】:Insert records based on last entry of foreign table根据外部表的最后一个条目插入记录
【发布时间】:2018-05-27 11:00:18
【问题描述】:

我有以下简单的表关系:

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 Enum转换而来,可以是:INITIALIZED、AVAILABLE、PROCESSED等

我想要执行以下操作:如果产品的最后一个事件条目具有状态“已处理”,则在状态为“可用”的 EVENT 表中创建一个新插入。这必须适用于所有产品。

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

但是想不通。

【问题讨论】:

    标签: java sql postgresql postgresql-9.5


    【解决方案1】:

    如果您的数据是按顺序插入的,我更倾向于使用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
                         );
    

    我更感兴趣的是你说过stateenum。 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'))
    );
    

    【讨论】:

    • 太好了,正是我想要的!数据插入是顺序的,我所说的 Enum 是 Java Enum。我更新了帖子。再次感谢!
    【解决方案2】:
    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
    );
    

    如果该命令要在多线程/多用户环境下运行,那么整个事务必须由这四个命令组成,否则可能会出现重复记录:

    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;
    

    【讨论】:

    • 感谢您的回答。 Gordon Linoff 的答案正是我想要的。
    猜你喜欢
    • 1970-01-01
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    相关资源
    最近更新 更多