在更新行之前复制行以保留Postgres中的存档

时间:2012-03-30 03:06:16

标签: postgresql

我正在尝试创建一个保存每行版本的表。我们的想法是能够查询行在任何时间点的行,即使查询具有JOIN。考虑一个系统,其中主要资源是书籍,即书籍被查询,并且作者信息随身携带

CREATE TABLE authors (
    author_id INTEGER NOT NULL,
    version INTEGER NOT NULL CHECK (version > 0),
    author_name TEXT,
    is_active BOOLEAN DEFAULT '1',
    modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (author_id, version)
)

INSERT INTO authors (author_id, version, author_name) 
VALUES  (1, 1, 'John'),
        (2, 1, 'Jack'),
        (3, 1, 'Ernest');

我希望能够更新以上内容

UPDATE authors SET author_name = 'Jack K' WHERE author_id = 1;

并以

结束
2, 1, Jack, t, 2012-03-29 21:35:00
2, 2, Jack K, t, 2012-03-29 21:37:40

然后我可以用

查询
SELECT author_name, modified_on 
FROM authors
WHERE 
author_id = 2 AND 
modified_on < '2012-03-29 21:37:00' 
ORDER BY version DESC 
LIMIT 1;

获取

2, 1, Jack, t, 2012-03-29 21:35:00

以下内容并不真正起作用

CREATE OR REPLACE FUNCTION archive_authors() RETURNS TRIGGER AS $archive_author$
    BEGIN
        IF (TG_OP = 'UPDATE') THEN

            -- The following fails because author_id,version PK already exists
            INSERT INTO authors (author_id, version, author_name)
            VALUES (OLD.author_id, OLD.version, OLD.author_name);

            UPDATE authors 
            SET version = OLD.version + 1
            WHERE 
                author_id = OLD.author_id AND 
                version = OLD.version;
            RETURN NEW;
        END IF;
        RETURN NULL; -- result is ignored since this is an AFTER trigger
    END;
$archive_author$ LANGUAGE plpgsql;

CREATE TRIGGER archive_author
AFTER UPDATE OR DELETE ON authors
    FOR EACH ROW EXECUTE PROCEDURE archive_authors();

我如何实现上述目标?或者,有没有更好的方法来实现这一目标?理想情况下,我宁愿不创建影子表来存储存档的行。

3 个答案:

答案 0 :(得分:2)

使用BEFORE UPDATE触发器(而不是AFTER UPDATE)相对容易。

但这也意味着您必须手动确保在该表的所有其他BEFORE UPDATE触发器之后触发归档触发器。触发器按字母顺序触发。

答案 1 :(得分:0)

我无法想出一个替代你的方法,可能是不同的执行,但基本的想法将IMO始终是相同的。如果您希望能够使用上面显示的用于查询特定版本的查询,则必须坚持使用单个表和发布的触发器。

也许也可以考虑这个问题:拥有一个表authors_revisions来存储你需要的所有修订版(触发器略有改动)。因此,原始表将始终具有最多tp-date条目。您可以创建一个连接两个表的视图,以便执行上面显示的查询。这样,您的主表不会与旧版本混在一起。

答案 2 :(得分:0)

不要使用触发器并插入而不是更新:

insert into authors (author_id, version, author_name)
select 
    1, 
    (
        select max(version) + 1
        from authors
        where author_id = 1
    ),
    'Jack K'

BTW为什么有时间戳的版本号?