2011-02-11 17 views
6

Her tabloya bir veri sürümü veya tarih özelliği eklemek zorunda olduğum bir proje üzerinde çalışıyorum. Temel olarak, veritabanındaki her ekleme veya değişikliği takip etmeliyiz, böylece her tablodaki verilerin önceki sürümlerine geri dönmesi veya görüntülenmesi kolaydır.Veritabanı tablosu için bir sürüm/tarih sistemi ekleme

Proje yöneticimin bunu tasarlama biçiminin yapılması, her tabloya birkaç yeni kolonun eklenmesidir. Ana özellik "version" adlı bir kolonudur. Bir güncelleme yapıldığında, hiçbir şey gerçekten güncellenmez, eski satır kalır ancak tabloya "sürüm" için artırılmış bir değerle yeni bir satır eklenir.

Geçerli verileri göstermek için, yalnızca her türdeki en yüksek sürüm numaralı satırları gösteren bir görünüm kullanıyoruz.

Bu, farklı sürümler arasında ileri ve geri gitme kadar mükemmel çalışırken, bu yaklaşımla ilgili bir sorunla karşılaştım. Tablolar arasında var olan herhangi bir ilişki için, yabancı anahtarları tanımlamamız gerekir ve yabancı anahtarlar yalnızca diğer tablodaki benzersiz alanları referans alabilir. Şimdi aynı satırın birkaç versiyonunu tutuyoruz (uygulama ile ilgili olarak aynı veri parçasıyla aynı 'Id' ile), artık başka bir tablonun 'Id' değerini yabancı bir anahtar olarak kullanamayız. bir masa.

Her satır için benzersiz bir birincil anahtar alanı kullanıyoruz, ancak birkaç satır aynı şeylerin temel olarak farklı sürümleri olduğundan, bir tanımlayıcı olarak yararsızdır. Her tür girişin en son versiyonunu manuel olarak takip edebilir ve her zaman bir şey değiştiğinde karşılık gelen yabancı anahtar ilişkilerini güncelleyebiliriz, fakat bu çok işe yaramış gibi gözüküyor ve her zaman işe yarayacağından emin değilim (örneğin önceki versiyona geri dönme) Bir giriş başka bir tablodaki başka bir girdinin eski ve kullanılamaz bir sürümüne başvuruda bulunulmasına neden olabilir.)

Veritabanı güncelleştirmelerinin geçmişini tutmanın başka yolları olduğunu biliyorum (örneğin, farklı bir tarih tablosunu kullanarak her bir tablo), ancak bu projede bu yaklaşıma bağlı kaldım. Eksik olduğum tablolar arasındaki ilişkilerin daha açık bir yolu var mı?

Not: MS SQL Server 2008 R2 kullanıyorum.

cevap

4

MySQL için sürüm üzerinde iyi bir makale vardır: http://www.jasny.net/articles/versioning-mysql-data/

Ben temelleri kolayca

+0

Teşekkürler, ama bu gerçekten sürüm oluşturma verilerinin tamamen farklı bir yoludur. Bu makalede kullanılan şema, ayrı bir revizyon tablosu kullanmaktadır. Konuştuğum yol, her şeyi aynı tabloda tutmaktır. Her halükarda, farklı bir versiyon versiyonuna yerleştik (makaleye çok benzer). – MAK

1

bir "ayrı revizyon tablosu" istemediğini söylemek başka sisteme uygulanabilir olduğunu düşünüyorum, hiç oylama değil FraktalizeR'nin çözümü bu çünkü. Tamam, burada bir "bir masa çözümü" ... Ama, lütfen, daha iyi cevaplar ve tüm ziyaretçiler için bu sayfanın daha iyi kullanımı için sorunuzu basitleştirin/genelleştirin: Sorunun SQL tablolarında "revizyon kontrolü" ile ilgili olduğunu düşünüyorum.

"ISO 2008 SQL" çözümü, daha sonra Microsoft SQL Server için de geçerli. PostgreSQL 9.1'de test ettim. Sorunun bu tür orijinal tablo "taklit" bir SQL görünümü kullanabilirsiniz yılında

ve daha özelliklere sahip bir yenisi olarak "sürüm tablo": * sort (sipariş) için bir yeni özellik moment revizyonlar ve zaman kaydı için; * "İzlenebilirlik" için yeni bir özellik cmd (gerçekten gerekli değil).

Orijinal (ve geleneksel) tablonuzun t olduğunu varsayalım. Düzeltme kontrolü için yeni özellikler eklemelisiniz, ancak diğer programcılar bu yeni özellikleri görmeye gerek yok ...Çözüm, t tablosunu t_hist olarak yeniden adlandırmak ve diğer programcılara SQL VIEW t (t_hist üzerinden sorgu olarak) sunmaktır.

t, geleneksel bir tabloyu göstermek için bir GÖRÜNÜMdür: sadece "current tuples". t_hist "tarih tuples" ile yeni bir tablodur. A, b.

t. PS: on t_histt üzerinde daha iyi performans için isTop ekledim.

-- .... 
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; 

Not: Bana, tetik çok karmaşık olacak bir görünümü olmayan bir tablo için bu çözümü uygulamaktan denemek için değil önermek; t_hist'a eklenen tüm içeriğin t'a kopyalanacağı t_hist için uyarlamaya çalışmayın.

+0

Soruma "kişisel ayrıntılar" olarak adlandırılabilecek hiçbir şey göremiyorum. Bilgimin benim problemimi belirtmek için gerekli olan minimum olduğuna inanıyorum. Belki de çok büyüktü - ama kesinlikle "kişisel bilgi" yoktu. Yanıtlama zamanını aldığınız için teşekkür ederim, ama benim sorduğum soruda belirttiğim gibi, önerdiğiniz uygulamadaki sorun, böyle bir programda yabancı anahtar ilişkilerini nasıl temsil edeceğinizdir. Ek bilgi içeren tablolardan görünümler, nasıl yapıldığını biliyorum. Her neyse, bu bir yıldan fazladır ve FractalizeR'nin yöntemi gibi bir şeyle gittim. Ama hepinize teşekkürler. – MAK

+0

Merhaba, "kişisel" kelimesini kabul ediyorum ve onu kaldırdım. İlk yorumlarım siteyi ve soru oranlarınızı iyileştirmekti. VIEW kullanımı hakkında, başka bir tablo değil, sadece tetikleyiciler ve harici kullanıcılar için iyi bir cephe. Çözüm bir "bir masa çözümü" (!). "Saf SQL" çözümleri için, sizin için iyi bir şey varsa (MS-SQL-server kullanıcısı olarak) http://stackoverflow.com/questions/9481557 veya http: // stackoverflow gibi başka yanıtlar için burada yorum yapabilirsiniz. com/questions/503472 –

+0

@Peter Krauss: Zarif ve iyi açıklanmış bir çözüm: TEŞEKKÜRLER :) –

İlgili konular