당신은 "별도의 수정 테이블을"하지 말 다른 시스템에 적용 할 수있는 생각에 투표하지 않음 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_hist
t
의 성능 향상을 위해 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
으로 복사됩니다.
감사하지만 실제로는 완전히 다른 방식의 데이터 버전 관리입니다. 이 기사에서 사용 된 스키마는 별도의 개정 테이블을 사용하는 반면, 동일한 테이블에서 모든 것을 유지하는 것을 목표로 삼고 있습니다. 어쨌든, 우리는 버전 관리의 다른 방식으로 정했습니다 (이 기사의 내용과 매우 유사합니다). – MAK