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,检查NEWOLD 值可以做你想做的事。 不,你不能有一个知道除了行本身之外的任何事情的约束。您可以基于行中的列设置约束,但不能在其他表中,也不能在过去。 【参考方案1】:

使用触发器。这是一个简单的 PL/PgSQL ON UPDATE ... FOR EACH ROW 触发器的完美工作,它可以看到 NEWOLD 值。

见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 约束,以防止根据其先前值更新列的主要内容,如果未能解决你的问题,请参考以下文章

SQL 根据两列删除重复记录

SQL:更新时如何判断和跳过唯一约束?

自定义 tableviewcell 动态高度未根据约束更新(以编程方式自动布局)

使用新列更新 sql 表

ruby 更新:更改所有适用的方法以防止SQL注入

ruby 更新:更改所有适用的方法以防止SQL注入