2016-07-01 62 views
1

我有一些我想爲其版本數據的表。就設計而言,這些只是插入表格。該表是這樣的使用Oracle進行數據版本管理

TABLENAME 
---------- 
SURROGATE_KEY_ID 
USER_VISIBLE_NATURAL_KEY_ID 
VERSION 
VALUE 

我要的是每當我插入一條記錄,新的記錄應該有一個版本比以前的版本最大爲給定USER_VISIBLE_NATURAL_KEY_ID一套更大的。我對版本的期望是能夠從舊數據中分辨新數據,同時保留舊數據。你知道,標準的歷史追蹤,審計還是有些東西。

我試着像

CREATE OR REPLACE TRIGGER TABLENAME_TRIGGER 
BEFORE INSERT ON TABLENAME 
REFERENCING NEW AS NEW 
FOR EACH ROW 
DECLARE 
    LATEST VERSION NUMBER; 
BEGIN 
    SELECT NVL(MAX(VERSION), 0) INTO LATEST_VERSION FROM TABLENAME 
    WHERE TABLE_NAME.USER_VISIBLE_NATURAL_KEY_ID = :NEW.USER_VISIBLE_NATURAL_KEY_ID; 
    :NEW.VERSION = LATEST_VERSION +1; 
END; 

觸發這給了我一個變異表的錯誤,我想是因爲我想偷窺TABLENAME找到MAX(版本),而插入新行。

我該如何使這種方法奏效,或者如果我完全錯了頭,有什麼更好的方法?

+0

怎麼樣一個獨立的序列自動填充'VERSION'列? 「VERSION」列將提供「USER_VISIBLE_NATURAL_KEY_ID」的排序,而不管間隙。即爲什麼'VERSION'必須是下一個價值而不是簡單的價值? – Glenn

+0

這是一個有趣的想法。你會如何建議處理序列翻轉?注意它何時發生並在此之前存檔舊數據? – monknomo

+0

如果有人擔心翻車,我通常會根據日期和順序創建一個由2部分組成的標識符。然後按ID,時間戳,seq排序。將日期/時間戳設置爲適當的粒度,並且不需要擔心順序翻轉。 – Glenn

回答

0

如果Glenn回答了這個問題,我會接受,但替代的是,我已經當選爲使序列從版本拉。我也創建了一個抽象的版本在1

東西是一系列連續的起點一樣

SELECT 
    id, 
    user_visible_natural_key_id, 
    (SELECT count(b.id) 
    FROM tablename b 
    WHERE b.id < a.id 
    AND b.database_created_datetime <= a.database_created_datetime 
    AND b.version < a.version 
    AND b.user_visible_natural_key_id = a.user_visible_natural_key_id) + 1 
    AS version, 
value, 
database_created_datetime 
FROM tablename a 

這給我的東西,我可以從中選擇版本1或2的圖。

我不是我的觀點完全幸福了,但我打敗它我會更新這個答案有點

0

我會考慮尋找到Oracle的Flashback Data Archive功能代替,可由於Oracle版本11

它將使您入座很自然的模型(無需版本列或觸發器),而容易很加入可配置的更改歷史跟蹤。然後,您可以使用閃回查詢來查看過去的數據。它非常強大。

+0

One Note: 閃回數據存檔需要Oracle EE。自從oracle 12c以來,我強烈推薦這個功能。我在11g中看到了大量的錯誤和限制。 –

+0

@Evgeniy:感謝您的反饋。關於僅在EE中提供的功能,我現在無法檢查,但基於我閱讀的內容,該功能應該在所有版本中免費提供。儘管在EE中確實可以獲得更高級的功能。請參閱小標題[許可更改(免費提供所有數據庫版本)](https://oracle-base.com/articles/12c/flashback-data-archive-fda-enhancements-12cr1)。 – sstan

+0

看起來你是對的。我基於此鏈接https://docs.oracle.com/database/121/DBLIC/editions.htm#DBLIC109上的許可知識。在那篇文章中寫道:「基本閃回數據存檔在所有版本中,閃回數據存檔的優化需要EE和高級壓縮選項」,我錯過了 –

1

而不是在您的表中有版本號,您可以嘗試實施有效的從日期和有效的日期。插入記錄的新版本時,將舊記錄的有效日期更新爲SYSDATE,而對於新版本,更新從日期生效爲SYSDATE,生效日期爲NULL。迄今爲止有效的記錄是您的最新記錄。在這種方法中,您還可以使用SQL查詢生成版本號(邏輯)(以防您想在報告中顯示此版本號)。

由於某種原因,如果你想維護你的表中的版本號,序列將是一個更好的選擇。爲每個這樣的表創建一個序列,並在插入新記錄的同時在插入語句本身中使用sequence.NEXTVAL。這樣你將完全避免觸發器的開銷。雖然,在這種情況下,您將無法保證沒有間隙的版本(如果在插入序列後產生間隙,有人回滾事務)。

+0

這與我所實施的東西類似,我並不完全滿意。我有一個「database_create_date」和一個視圖,它通過計算該日期之前有多少日期來推斷版本。它只有毫秒的分辨率,有時我們會得到版本號衝突,這對我們來說不起作用。有效日期如何處理兩個同時插入(或更新)? – monknomo

1

我會嘗試回答問題「我怎樣才能使這種方法有效?」。 主要麻煩製造者是條件「每當我插入一條記錄時,新記錄的版本應該比先前的最大版本的版本更大」。所以我們不能使用序列來有效地處理併發。相反,我們不得不請求某種顯式鎖定(例如,在對象的第一版本上排他鎖定)。鎖的擁有者能夠選擇該對象的最大版本而沒有風險而不會獲得實際的數據。因此,例如,您可以使用select for update + insert而不是insert +觸發器。或者使用包API來創建對象的新版本。

UPD:例如,對於簡單的表tab(id, val, ver)

select * from tab where id = :id and ver = 0 for update;

insert into tab(id, val, ver) values(:id, :new_val, (select max(ver) + 1 from tab where id = :id));

+0

您可以擴展選擇更新+插入嗎?我不確定我是否承諾 – monknomo

相關問題