скопируйте строки перед обновлением, чтобы сохранить архив в Postgres - PullRequest
0 голосов
/ 30 марта 2012

Я экспериментирую с созданием таблицы, в которой хранится версия каждой строки. Идея состоит в том, чтобы иметь возможность запрашивать, как строки были в любой момент времени, даже если запрос имеет 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 ]

2 голосов
/ 30 марта 2012

Это относительно легко сделать с помощью триггера BEFORE UPDATE (вместо AFTER UPDATE).

Но это также означает, что вы должны вручную убедиться, что ваш архивный триггер запущен после всех других триггеров BEFORE UPDATE для этой таблицы. Триггеры срабатывают в алфавитном порядке.

0 голосов
/ 30 марта 2012

Не используйте триггер и вставляйте вместо обновления:

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

Кстати, зачем вам номер версии, если у вас есть отметка времени?

0 голосов
/ 30 марта 2012

Я не могу придумать альтернативу вашему подходу, возможно, другое исполнение, но основная идея IMO всегда будет одинаковой. Если вы хотите использовать запрос, который вы показали выше, для запроса определенной версии, вам нужно придерживаться одной таблицы и вашего опубликованного триггера.

Вы , возможно, также могли бы подумать об этом: иметь таблицу authors_revisions, в которой хранятся все необходимые вам ревизии (с немного измененным триггером). Таким образом, ваша исходная таблица всегда будет содержать самые последние записи. Вы могли бы создать представление, объединяющее обе ваши таблицы, чтобы выполнить запрос, показанный выше. Таким образом, ваша основная таблица не будет загромождена старыми версиями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...