有条件地将新记录插入事务 table
Conditional insert of new records into transaction table
我有一个 table 的历史交易,我想从一个新的 table 中插入记录,其中新记录的状态不同于新记录时的旧记录记录是 "valid"(基于两个 table 中的 valid_from
列)。
例如,取standing/transactional table(我就叫它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
table 的更新:
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
理想情况下,我想插入 only 新 table 的第 2 行,因为它的状态与具有相同 key
的记录不同old
table 与新记录之前的最近 valid_from
日期。具体来说:
id
= 1 在新的table 不应该被插入。键 A
('2014-01-01') old
table 中的最大 valid_from
日期在新的 valid_from
日期('2014- 01-15');即,它们的状态均为 x
.
id
= 2 in the new table should be inserted because its status
(x
) 不同于old
table (y
) 中的 status
其中旧 table 中的 valid_from
日期等于最大值 valid_from
对于新 table. 中 valid_from
之前的那个键
id
= 3 不应插入,因为它与最近 valid_from
[的旧 table 中的记录具有相同的状态 (x
) =91=]
id
= 4 不应插入,因为它的状态 (y
) 与 old
table.[= 中的相应记录相同91=]
插入后,old
table 应如下所示:
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
目标:按 valid_from
.[升序排序时,绝不应该有两个具有相同 key
和 status
的连续记录=59=]
如果我的想法是简单地插入 key
+ status
组合的记录,而 old
table 中不存在,我会这样做:
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;
然而,这错过了新 table 中的 id
2,因为旧 table 中的 id
1 具有相同的状态,即使有更新的old
table 中的记录确实不同。
无论我尝试什么,我似乎都无法将 new
记录与 只有 最 "recent" old
记录进行比较valid_from
< 新记录中的 valid_from
。我有一种感觉,我可以通过组合 window 函数 and/or 和 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
table 中的第一条记录:
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。关于解决这个问题的方法有什么建议吗?提前致谢!
经过更多的尝试,我想我有一个解决方案(虽然可能不是最好的)。步骤的逻辑顺序是:
- 在键上加入,仅在新记录的
valid_from
大于旧记录的 valid_from
的情况下
- 对于每个匹配项,包括一个标志,用于指示旧记录的
valid_from
是否是所有候选匹配项中最近的 valid_from
(我们称之为 is_most_recent
)。我使用 window 函数(不能在 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;
我很想知道是否有更好的解决方案。
我有一个 table 的历史交易,我想从一个新的 table 中插入记录,其中新记录的状态不同于新记录时的旧记录记录是 "valid"(基于两个 table 中的 valid_from
列)。
例如,取standing/transactional table(我就叫它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
table 的更新:
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
理想情况下,我想插入 only 新 table 的第 2 行,因为它的状态与具有相同 key
的记录不同old
table 与新记录之前的最近 valid_from
日期。具体来说:
id
= 1 在新的table 不应该被插入。键A
('2014-01-01')old
table 中的最大valid_from
日期在新的valid_from
日期('2014- 01-15');即,它们的状态均为x
.id
= 2 in the new table should be inserted because itsstatus
(x
) 不同于old
table (y
) 中的status
其中旧 table 中的valid_from
日期等于最大值valid_from
对于新 table. 中 id
= 3 不应插入,因为它与最近valid_from
[的旧 table 中的记录具有相同的状态 (x
) =91=]id
= 4 不应插入,因为它的状态 (y
) 与old
table.[= 中的相应记录相同91=]
valid_from
之前的那个键
插入后,old
table 应如下所示:
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
目标:按 valid_from
.[升序排序时,绝不应该有两个具有相同 key
和 status
的连续记录=59=]
如果我的想法是简单地插入 key
+ status
组合的记录,而 old
table 中不存在,我会这样做:
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;
然而,这错过了新 table 中的 id
2,因为旧 table 中的 id
1 具有相同的状态,即使有更新的old
table 中的记录确实不同。
无论我尝试什么,我似乎都无法将 new
记录与 只有 最 "recent" old
记录进行比较valid_from
< 新记录中的 valid_from
。我有一种感觉,我可以通过组合 window 函数 and/or 和 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
table 中的第一条记录:
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。关于解决这个问题的方法有什么建议吗?提前致谢!
经过更多的尝试,我想我有一个解决方案(虽然可能不是最好的)。步骤的逻辑顺序是:
- 在键上加入,仅在新记录的
valid_from
大于旧记录的valid_from
的情况下
- 对于每个匹配项,包括一个标志,用于指示旧记录的
valid_from
是否是所有候选匹配项中最近的valid_from
(我们称之为is_most_recent
)。我使用 window 函数(不能在 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;
我很想知道是否有更好的解决方案。