有条件地将新记录插入到事务表中

时间:2015-01-26 16:49:57

标签: postgresql join transactional

我有一个历史交易的常设表,我想从一个新表中插入记录,其中新记录的状态与新记录的有效时间不同。#34;有效" (基于两个表中的valid_from列)。

例如,采取常设/交易表格(我称之为old):

CREATE TABLE old (id serial, key text, valid_from date, status text);
INSERT INTO old (key, valid_from, status)
VALUES
('A', '2014-01-01', 'x'),
('A', '2014-02-01', 'y'),
('A', '2014-03-01', 'z'),
('B', '2014-01-10', 'x'),
('B', '2014-02-15', 'y');

SELECT * FROM old;

 id | key | valid_from | status
----+-----+------------+--------
  1 | A   | 2014-01-01 | x
  2 | A   | 2014-02-01 | y
  3 | A   | 2014-03-01 | z
  4 | B   | 2014-01-10 | x
  5 | B   | 2014-02-15 | y

new更新表:

CREATE TABLE new (id serial, key text, valid_from date, status text);
INSERT INTO new (key, valid_from, status)
VALUES
('A', '2014-01-15', 'x'),
('A', '2014-02-15', 'x'),
('A', '2014-03-15', 'z'),
('B', '2014-02-15', 'y');

SELECT * FROM new;

 id | key | valid_from | status
----+-----+------------+--------
  1 | A   | 2014-01-15 | x
  2 | A   | 2014-02-15 | x
  3 | A   | 2014-03-15 | z
  4 | B   | 2014-02-15 | y

理想情况下,我想插入新表的第2行,因为它的状态与key表中具有相同old的记录的状态不同新记录之前的最近valid_from日期。具体做法是:

    不应插入新表中的
  • id = 1。新valid_from日期之前old(' 2014-01-01')A表中的最长valid_from日期(&# 39; 2014年1月15日&#39);即,他们的状态均为x
  • id = 2新表应插入,因为其statusx)与status中的old不同{1}}表(y)其中旧表中的valid_from日期等于新密钥中valid_from之前的那个密钥的最大valid_from
  • 不应插入
  • id = 3,因为它与旧表中最近x
  • 的记录共享相同的状态(valid_from) 不应插入
  • id = 4,因为它的状态(y)与old表中的相应记录相同。

插入后,old表应如下所示:

SELECT * FROM old ORDER BY key, valid_from;

 id | key | valid_from | status
----+-----+------------+--------
  1 | A   | 2014-01-01 | x
  2 | A   | 2014-02-01 | y
  6 | A   | 2014-02-15 | x
  3 | A   | 2014-03-01 | z
  4 | B   | 2014-01-10 | x
  5 | B   | 2014-02-15 | y

目标:按升序key排序时,永远不会有两个连续的记录statusvalid_from

如果想法只是简单地插入key + status组合中old表中不存在的记录,我会这样做:

INSERT INTO old (key, valid_from, status)
SELECT new.key, new.valid_from, new.status
FROM new LEFT JOIN old USING(key, status)
WHERE old.key IS NULL;

但是,由于旧表中的id 1共享相同的状态,因此在新表中错过id 2,即使old表中有更新的记录,确实有所不同。

无论我尝试什么,我似乎都无法检查new记录最多"最近"标old< valid_from新记录中的valid_from。我有一种感觉,我可以通过窗口函数和/或DISTINCT ON的组合来找到最大的valid_from,但我似乎无法将两者结合在一起。例如,我尝试过这样的事情:

INSERT INTO old(key, valid_from, status)
SELECT n.key, n.valid_from, n.status
FROM new n LEFT JOIN (
    SELECT DISTINCT ON (old.key) old.*
    FROM new
        JOIN old
        ON old.key = new.key
        AND old.valid_from < new.valid_from
        ORDER BY old.key, old.valid_from DESC) o
ON
    n.key = o.key
    AND n.status = o.status
WHERE
    o.key IS NULL;

然而,这最终会插入new表中的第一条记录:

 id | key | valid_from | status
----+-----+------------+--------
  1 | A   | 2014-01-01 | x
  6 | A   | 2014-01-15 | x
  2 | A   | 2014-02-01 | y
  7 | A   | 2014-02-15 | x
  3 | A   | 2014-03-01 | z
  4 | B   | 2014-01-10 | x
  8 | B   | 2014-02-15 | y
  5 | B   | 2014-02-15 | y

我正在使用PostgreSQL 9.3。关于解决这个问题的任何指针?提前谢谢!

1 个答案:

答案 0 :(得分:0)

经过一些玩弄之后,我想我有一个解决方案(虽然可能不是最好的解决方案)。步骤的逻辑顺序是:

  1. 加入密钥,只有新记录的valid_from大于旧记录的valid_from
  2. 对于每场比赛,请在所有候选比赛(我们称之为valid_from)中包含旧唱片valid_from是否是最新is_most_recent的标志。我使用窗口函数(不能在WHERE子句中使用)来完成此操作。
  3. 确保新的status不等于旧的status
  4. 插入这些记录
  5. 这是迄今为止我提出的最好的方法:

    INSERT INTO old (key, valid_from, status)
    SELECT key, valid_from, status FROM (
        SELECT
            new.key, 
            new.valid_from, 
            new.status,
            -- Does the new record have a different statusf from the old?
            old.status != new.status AS is_different_status, 
            -- Are we comparing the most "recent" record in the old table?
            old.valid_from = MAX(old.valid_from) OVER (PARTITION BY old.key, new.id)
                AS is_most_recent
        FROM new JOIN old
            ON new.key = old.key
            AND new.valid_from >= old.valid_from) AS to_insert
    WHERE is_different_status AND is_most_recent;
    

    我很想知道那里是否有更好的解决方案。