我们有一些中等到大的表(即数百行的数据)我们希望用户能够有效地使用#fork;#34;与Git类似,并可能随着时间的推移进行协作。我们希望他们能够" fork"它们多次,并对不同的叉子进行编辑,并比较两个叉子之间的各种聚合数据。
基本上,这些数据以一组只读表开始,我们希望让用户能够查看包含其编辑的表。我们面临的挑战是,我们还要定期查询此表中的行(即仅显示column3 =' left')的行,甚至可能根据特定列连接另一个表。 (即INNER JOIN,其中user_table.column3 = other_table.column10) - 即我们希望能够将这些表视为关系操作的完全物化表。
最愚蠢的解决方案(我们今天做的)只是制作表格的完整副本,但挑战是至少在我们目前的版本中这是昂贵的:我们使用PostgreSQL,这些复制操作可以采取2- 20分钟。我们希望这是一个实时操作,就像具有写时复制行为的东西一样。我们会记录用户所做的更改(即更改日志),以便我们最终将它们应用于"原始"表格,但有一个模式,或理想的世界,图书馆或存储层,这只是为我们这样做。
我们今天碰巧使用PostgreSQL和Python,但我在这里对NoSQL系统开放,因为我可以想象这可能导致一些非常讨厌的SQL,如果这足够概括的话。此外,我们愿意牺牲一些关系能力来实现上述目标。这个领域是否有已知的模式和/或实现?在PostgreSQL或其他存储系统中?事实证明这对谷歌来说真的很难。
答案 0 :(得分:1)
当然可以使用它做一些事情,虽然结果可能(或可能不)是片状的,这取决于你将在数据库中使用多少其他黑客。
如果我们将列owner
和deleted
添加到源表中,请创建一个视图,向视图添加INSTEAD OF
个触发器,并仅向视图授予用户权限,而不是源表我们得到这个:
CREATE SEQUENCE source_seq;
CREATE TABLE source
(
id INT DEFAULT nextval('source_seq')
,value VARCHAR
,owner name DEFAULT session_user
,deleted boolean DEFAULT FALSE
);
CREATE VIEW source_emp AS
SELECT id, value
FROM source AS s1
WHERE ((owner IS NULL AND NOT EXISTS (SELECT * FROM source AS s2 WHERE s1.id = s2.id AND s2.owner = session_user )) OR owner = session_user)
AND NOT deleted
CREATE OR REPLACE FUNCTION source_change()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
BEGIN
IF TG_OP = 'UPDATE' THEN
INSERT INTO source(id,value,owner,deleted)
SELECT NEW.id,NEW.value,session_user, FALSE
WHERE NOT EXISTS(SELECT * FROM source WHERE owner = session_user AND id = OLD.id);
UPDATE source SET id = NEW.id, value = NEW.value, owner = session_user WHERE owner = session_user AND id = OLD.id;
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO source(id,value,owner,deleted)
SELECT OLD.id,NULL,session_user, TRUE
WHERE NOT EXISTS(SELECT * FROM source WHERE owner = session_user AND id = OLD.id);
UPDATE source SET value = NULL, deleted = TRUE WHERE owner = session_user AND id = OLD.id;
RETURN NULL;
END IF;
RETURN NEW;
END;
$function$;
CREATE TRIGGER source_trig
INSTEAD OF UPDATE OR DELETE ON
source_emp FOR EACH ROW EXECUTE PROCEDURE source_change();
现在,如果用户尝试:
如果您不想触摸原始表格,您可以使用其他两列创建一个新表格并更改视图,使其与原始表格结合使用。