我有一个历史交易的常设表,我想从一个新表中插入记录,其中新记录的状态与新记录的有效时间不同。#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新表应插入,因为其status
(x
)与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
排序时,永远不会有两个连续的记录status
和valid_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。关于解决这个问题的任何指针?提前谢谢!
答案 0 :(得分:0)
经过一些玩弄之后,我想我有一个解决方案(虽然可能不是最好的解决方案)。步骤的逻辑顺序是:
valid_from
大于旧记录的valid_from
valid_from
)中包含旧唱片valid_from
是否是最新is_most_recent
的标志。我使用窗口函数(不能在WHERE子句中使用)来完成此操作。 status
不等于旧的status
这是迄今为止我提出的最好的方法:
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;
我很想知道那里是否有更好的解决方案。