2013-01-16 60 views
4

可以使用檢查約束(或某種其他技術)來防止在更新其記錄時設置與其先前值相矛盾的值。防止根據先前值更新列的SQL約束

一個例子是一個NULL時間戳,指示發生了什麼,如「file_exported」。一旦文件被導出並具有非NULL值,它不應該再次設置爲NULL。

另一個例子是一個計數器,其中一個整數只允許增加,但不能減少。

如果它有助於我使用PostgreSQL,但我想看到適合任何SQL執行

+0

到目前爲止您是否研究過任何內容? –

+5

可能最好使用觸發器。檢查當前值是否爲null,如果嘗試將其設置爲null,則會引發錯誤。雖然如果你需要一個歷史更改日誌,你必須自己構建它。 –

+0

你正在使用哪些DBMS?甲骨文? PostgreSQL的? –

回答

3

一個例子是NULL時間戳表示發生了什麼, 像「file_exported」。一旦文件被導出並且具有非NULL值,它不應再次設置爲NULL。

另一個例子是一個計數器,其中一個整數只允許增加 ,但不能減少。

在這兩種情況下,我都不會將這些更改記錄爲註釋表上的屬性; '出口'或'點擊數'是一個獨特的想法,代表了與它們相關的對象的相關但正交的現實世界概念:

所以它們只是不同的關係。因爲我們只想要 「file_exported」 發生一次:

CREATE TABLE thing_file_exported(
    thing_id INTEGER PRIMARY KEY REFERENCES(thing.id), 
    file_name VARCHAR NOT NULL 
) 

命中計數器同樣是一個不同的表:

CREATE TABLE thing_hits(
    thing_id INTEGER NOT NULL REFERENCES(thing.id), 
    hit_date TIMESTAMP NOT NULL, 
    PRIMARY KEY (thing_id, hit_date) 
) 

,你可能會與

SELECT thing.col1, thing.col2, tfe.file_name, count(th.thing_id) 
FROM thing 
LEFT OUTER JOIN thing_file_exported tfe 
    ON (thing.id = tfe.thing_id) 
LEFT OUTER JOIN thing_hits th 
    ON (thing.id = th.thing_id) 
GROUP BY thing.col1, thing.col2, tfe.file_name 
+0

這是設計中非常重要的一點。 – andyortlieb

+1

這不是我的問題的答案,但我必須接受它,因爲它是更好問題的正確答案。 – andyortlieb

+0

這是例子的正確答案,但不是問題的關鍵。此外,您仍然可以遞減計數器並刪除導出關係。這不應該被接受。 – frostymarvelous

1

在PostgreSQL的存儲過程和函數都可以訪問兩個新舊價值的解決方案,而且代碼可以訪問任意表和列。在存儲過程中構建簡單(粗糙的)有限狀態機並不困難。你甚至可以用這種方式構建表驅動的狀態機。

4

使用觸發器。這對於一個簡單的PL/PgSQL ON UPDATE ... FOR EACH ROW觸發器來說是一個完美的工作,它可以同時看到NEWOLD的值。

請參閱trigger procedures

3

lfLoop查詢有最好的解決這個問題。但要繼續使用觸發器Craig Ringer的方法,這裏是一個例子。本質上,您在更新之前將列的值設置回原始(舊)值。

CREATE OR REPLACE FUNCTION example_trigger() 
    RETURNS trigger AS 
$BODY$ 
BEGIN 
    new.valuenottochange := old.valuenottochange; 
    new.valuenottochange2 := old.valuenottochange2; 
    RETURN new; 
END 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 



DROP TRIGGER IF EXISTS trigger_name ON tablename; 
    CREATE TRIGGER trigger_name BEFORE UPDATE ON tablename 
    FOR EACH ROW EXECUTE PROCEDURE example_trigger();