你说,不希望有一个“独立的修订表”,而不是投票因为这个,FractalizeR的解决方案。好吧,这是一个“一个表格解决方案”...但是,请简化/概括您的问题,以便为所有访问者提供更好的答案和更好地使用此页面:我认为您的问题是关于SQL表格上的“修订控制”。
“ISO 2008 SQL”的解决方案,那么我认为它也适用于Microsoft SQL Server。我在PostgreSQL 9.1上测试了它。
在这样那样的问题,我们可以使用SQL视图来“模拟”原始表,以及“版本表”作为一个新的,更多的属性: *的排序(排序)的新属性moment
的修订和时间注册; *“追溯性”的新属性cmd
(不是必需的)。
假设您的原始(和传统)表格是t
。对于版本控制,你必须添加新的属性,但其他程序员不需要看到这个新属性...解决方法是将表t
重命名为t_hist
,并向其他程序员提供SQL VIEW t
(作为t_hist
的查询)。
t
是显示传统表的视图:只有“当前元组”。 t_hist
是具有“历史元组”的新表。
假设t
属性a,b。 PS: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
,其中插入t_hist
的所有内容都将复制到t
。
谢谢,但这实际上是一种完全不同的版本化数据的方式。该文章中使用的方案使用单独的修订表,而我所说的方式旨在将所有内容保存在同一个表中。无论如何,我们已经采用了不同的版本管理方式(非常类似于文章中的版本)。 – MAK 2011-02-14 13:48:04