SQL 约束,以防止根据其先前值更新列
Posted
技术标签:
【中文标题】SQL 约束,以防止根据其先前值更新列【英文标题】:SQL constraint to prevent updating a column based on its prior value 【发布时间】:2013-01-16 20:34:57 【问题描述】:是否可以使用检查约束(或其他一些技术)来防止在更新记录时设置与其先前值相矛盾的值。
一个例子是 NULL 时间戳,表示发生了一些事情,例如“file_exported”。一旦文件被导出并具有非 NULL 值,就不应再将其设置为 NULL。
另一个例子是命中计数器,其中整数只允许增加,但不能减少。
如果对我使用 postgresql 有帮助,但我希望看到适合任何 SQL 实现的解决方案
【问题讨论】:
到目前为止你有没有研究过什么? 最好为此使用触发器。检查当前值是否不为空,如果他们尝试将其设置为空,则抛出错误。不过,如果您需要历史更改日志,则必须自己构建.. 您使用的是哪个 DBMS?甲骨文? PostgreSQL? 一个简单的行触发器ON UPDATE
,检查NEW
和OLD
值可以做你想做的事。
不,你不能有一个知道除了行本身之外的任何事情的约束。您可以基于行中的列设置约束,但不能在其他表中,也不能在过去。
【参考方案1】:
使用触发器。这是一个简单的 PL/PgSQL ON UPDATE ... FOR EACH ROW
触发器的完美工作,它可以看到 NEW
和 OLD
值。
见trigger procedures。
【讨论】:
【参考方案2】: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();
【讨论】:
【参考方案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
【讨论】:
这不是我问题的答案,但我必须接受它,因为它是更好问题的正确答案。 这是示例的正确答案,但不是问题的正确答案。此外,您仍然可以减少计数器并删除导出关系。这不应该被接受。 请注意,在阻止file_exported
属性一旦定义后设置为 null
的情况下,此答案并不能解决主要问题:现在您必须阻止数据库删除thing_file_exported
的行。
这是一个有用的建议,但它并没有回答问题,也没有说明哪种设计更好。在不完全了解上下文的情况下,您不能肯定地说您建议的设计更好。你也可以用这个想法走得很远,将任何类型的更新视为独特的想法,并具有仅附加/不可变状态设计。他们都有自己的优点和缺点。【参考方案4】:
PostgreSQL 中的存储过程和函数可以访问旧值和新值,并且该代码可以访问任意表和列。在存储过程中构建简单的(粗略的?)有限状态机并不难。你甚至可以用这种方式构建表驱动的状态机。
【讨论】:
以上是关于SQL 约束,以防止根据其先前值更新列的主要内容,如果未能解决你的问题,请参考以下文章