2011-02-11 2 views
6

모든 테이블에 데이터 버전 관리 또는 기록 기능을 추가해야하는 프로젝트를 진행 중입니다. 기본적으로 우리는 데이터베이스의 모든 삽입 또는 변경 사항을 추적해야하므로 각 테이블에서 이전 버전의 데이터로 쉽게 롤백하거나 볼 수 있습니다.데이터베이스 테이블에 버전/기록 시스템 추가

내 프로젝트 관리자가 수행하는 방식은 각 테이블에 몇 가지 새로운 coloumns를 추가하는 것입니다. 주요 특징은 "버전"이라는 coloumn입니다. 업데이트가 수행 될 때마다 실제로는 아무 것도 업데이트되지 않고 이전 행은 그대로 유지되지만 "version"에 대해 증가 된 값을 가진 새로운 행이 테이블에 추가됩니다.

현재 데이터를 표시하기 위해 각 유형의 버전 번호가 가장 높은 행만 표시하는보기를 사용합니다.

다른 버전간에 앞뒤로 움직이는만큼 훌륭하게 작동하지만이 방법에 문제가 있습니다. 테이블간에 존재하는 관계가 있으려면 외래 키를 정의해야하며 외래 키는 다른 테이블의 고유 필드 만 참조 할 수 있습니다. 이제 동일한 행의 여러 버전을 유지하고 있습니다 (동일한 'ID'는 기본적으로 응용 프로그램과 관련하여 동일한 데이터 임). 우리는 더 이상 다른 테이블의 'Id'를 외래 키로 사용할 수 없습니다. 테이블.

우리는 각 행에 대해 고유 한 기본 키 필드를 사용하지만 여러 행이 기본적으로 동일한 항목의 다른 버전이므로 식별자로 쓸모가 없습니다. 우리는 수동으로 각 종류의 항목의 최신 버전을 추적하고 무언가가 변경 될 때마다 해당 외래 키 관계를 업데이트 할 수 있습니다. 그러나 그것은 많은 일처럼 보입니다. 그리고 항상 작동 할 것이라고 확신하지 못합니다 (예 : 이전 버전 어떤 다른 테이블에있는 다른 엔트리의 이전 버전과 사용 불가 한 버전을 참조 할 수 있습니다.)

다른 방법으로 데이터베이스 업데이트 기록을 유지할 수 있습니다. 각 테이블),하지만이 프로젝트 에서이 접근 방식에 붙어있다. 누락 된 테이블 간의 관계를 처리하는 좀 더 분명한 방법이 있습니까?

참고 : MS SQL Server 2008 R2를 사용하고 있습니다.

답변

4

MySQL 용 버전에 대한 좋은 기사가있다 : http://www.jasny.net/articles/versioning-mysql-data/

나는 그것이 기본이 쉽게

+0

감사하지만 실제로는 완전히 다른 방식의 데이터 버전 관리입니다. 이 기사에서 사용 된 스키마는 별도의 개정 테이블을 사용하는 반면, 동일한 테이블에서 모든 것을 유지하는 것을 목표로 삼고 있습니다. 어쨌든, 우리는 버전 관리의 다른 방식으로 정했습니다 (이 기사의 내용과 매우 유사합니다). – MAK

1

당신은 "별도의 수정 테이블을"하지 말 다른 시스템에 적용 할 수있는 생각에 투표하지 않음 FractalizeR의 솔루션 때문입니다. 좋아, 여기에 "하나의 테이블 솔루션"입니다 ... 그러나, 제발, 단순화/일반화하여 귀하의 질문에 대한 더 나은 답변과 모든 방문자에 대해이 페이지의 더 나은 사용 : 귀하의 문제는 SQL 테이블에 대한 "개정 제어"에 대한 것 같아요.

"ISO 2008 SQL"에 대한 해결책은 Microsoft SQL Server에 대한 것입니다. PostgreSQL 9.1에서 테스트했습니다. 이런 종류의 문제 우리가 원래의 테이블을 "모방"하는 SQL보기를 사용할 수 있습니다에서

, 그리고 더 많은 속성을 가진 새,와 "버전 표" * 종류 (주문)에 대한 새로운 속성 moment 수정 및 시간 등록; * "추적 가능성"을위한 새로운 속성 cmd (꼭 필요하지 않음).

원본 (및 기존) 테이블이 t이라고 가정합니다. 버전 관리를 위해서는 새로운 속성을 추가해야하지만 다른 프로그래머는이 새로운 속성을 볼 필요가 없습니다 ...해결 방법은 t 테이블의 이름을 t_hist으로 바꾸고 다른 프로그래머에게 t (보기는 t_hist 이상의 쿼리)을 제공하는 것입니다.

t은 기존 테이블을 보여주는보기입니다. "현재 튜플"만 있습니다. t_hist은 "기록 튜플"이있는 새로운 테이블입니다.

속성이 a, b 인 t으로 가정합니다. 추신 : t_histt의 성능 향상을 위해 isTop을 추가했습니다.

-- .... 
CREATE TABLE t_hist (
    -- the old attributes for t: 
    id integer NOT NULL, -- a primary key of t 
    a varchar(10), -- any attribute 
    b integer,  -- any attribute 

    -- new attributes for revision control: 
    isTop BOOLEAN NOT NULL DEFAULT true, -- "last version" or "top" indicator 
    cmd varchar(60) DEFAULT 'INSERT', -- for traceability 
    moment timestamp NOT NULL DEFAULT now(), -- for sort revisions 
    UNIQUE(id,moment) 
); 

CREATE VIEW t AS 
    SELECT id,a,b FROM t_hist WHERE isTop; 
    -- same, but better performance, as 
    -- SELECT id,a,b FROM t_hist GROUP BY id,a,b HAVING MAX(moment)=moment 

-- Verifies consistency in INSERT: 
CREATE FUNCTION t_hist_uniq_trig() RETURNS TRIGGER AS $$ 
DECLARE 
    aux BOOLEAN; 
BEGIN 
    SELECT true INTO aux FROM t_hist 
    WHERE id=NEW.id AND moment>=NEW.moment; 
    IF found THEN -- want removes from top? 
    RAISE EXCEPTION 'TRYING TO INCLUDE (ID=%) PREVIOUS TO %', NEW.id, NEW.moment; 
    END IF; 
    RETURN NEW; 
END $$ LANGUAGE plpgsql; 
CREATE TRIGGER uniq_trigs BEFORE INSERT ON t_hist 
    FOR EACH ROW EXECUTE PROCEDURE t_hist_uniq_trig(); 

CREATE FUNCTION t_reset_top(integer) RETURNS BOOLEAN AS $BODY$ 
    UPDATE t_hist SET isTop=false WHERE isTop=true AND id=$1 
    RETURNING true; -- null se nao encontrado 
$BODY$ LANGUAGE sql; 

-------- 
-- Implements INSER/UPDATE/DELETE over VIEW t, 
-- and controls unique id of t: 
CREATE OR REPLACE FUNCTION t_cmd_trig() RETURNS TRIGGER AS $$ 
DECLARE 
    aux BOOLEAN; 
BEGIN 
    aux:=true; 
    IF TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN 
    aux := t_reset_top(OLD.id); -- rets. true ou NULL 
    ELSE 
    SELECT true INTO aux FROM t_hist WHERE id=NEW.id AND isTop; 
    END IF; 
    IF (TG_OP='INSERT' AND aux IS NULL) OR (TG_OP='UPDATE' AND aux) THEN 
    INSERT INTO t_hist (id,a,b,cmd) VALUES (NEW.id, NEW.a,NEW.b,TG_OP); 
    ELSEIF TG_OP='DELETE' AND aux THEN -- if first delete 
    UPDATE t_hist SET cmd=cmd||' AND DELETE AT '||now() 
    ELSEIF TG_OP='INSERT' THEN -- fails by not-unique(id) 
    RAISE EXCEPTION 'REGISTER ID=% EXIST', NEW.id; 
    ELSEIF TG_OP='UPDATE' THEN -- .. redundance, a trigger not goes here 
    RAISE EXCEPTION 'REGISTER ID=% NOT EXIST', NEW.id; 
    END IF; 
    RETURN NEW; -- discarded 
END 
$$ LANGUAGE plpgsql; 
CREATE TRIGGER ins_trigs INSTEAD OF INSERT OR UPDATE OR DELETE ON t 
    FOR EACH ROW EXECUTE PROCEDURE t_cmd_trig(); 

-- Examples: 
INSERT INTO t(id,a,b) VALUES (1,'aaaaaa',3); -- ok 
INSERT INTO t(id,a,b) VALUES (1,'bbbbbb',3); -- error 
UPDATE t_hist SET a='teste' WHERE id=1;  -- ok 
    -- SELECT * from t;  SELECT * from t_hist; 
INSERT INTO t(id,a,b) VALUES 
    (2,'bbbbbb',22), -- ok 
    (3,'bbbbbb',22), -- ok 
    (4,'aaaaaa',2); -- ok 
DELETE FROM t WHERE id=3; 
    -- SELECT * from t;  SELECT * from t_hist; 

PS : 나는 당신의 트리거가 매우 복잡 할 것보기없이 하나 개의 테이블에이 솔루션을 적용하려고하지 않는 것이 좋습니다; t_hist을 으로 상속하려고 시도하지 마십시오. t_hist에 삽입 된 모든 콘텐츠는 t으로 복사됩니다.

+0

나는 내 질문에 "개인적인 세부 사항"이라고 할 수있는 것을 볼 수 없다. 나는 거기에있는 정보가 내 문제를 진술하는 데 필요한 최소한이라고 믿는다. 아마 그것은 너무 컸지 만 확실히 "개인 정보"는 없었습니다. 귀하의 답변에 시간을 내 주셔서 감사합니다,하지만 내 질문에 명시된 바와 같이, 당신이 제안 구현의 문제는 어떻게 그러한 제도에서 외래 키 관계를 나타내는 것입니다. 추가 정보가 포함 된 표에서보기, 이미 수행 방법을 알고 있습니다. 어쨌든, 이것은 1 년이 넘었고 FractalizeR의 방법과 같은 것을 사용했습니다. 하지만 고마워. – MAK

+0

안녕하세요, 저는 "개인적"이라는 단어에 동의하며 제거했습니다. 저의 초기 의견은 사이트와 질문 비율을 높이는 것이 었습니다. VIEW의 사용에 관해서는 다른 테이블이 아니며 트리거 및 외부 사용자에게 좋은 외관 일뿐입니다. 이 솔루션은 "하나의 테이블 솔루션"(!)입니다. "순수한 SQL"솔루션이 아닌 경우에는 MS-SQL-server 사용자 인 경우 http://stackoverflow.com/questions/9481557 또는 http : // stackoverflow와 같은 답변을 제공 할 수 있습니다. com/questions/503472 –

+0

@ Peter Krauss : 우아하고 잘 설명 된 솔루션 : 감사합니다 :) –

관련 문제