【问题标题】:SQL constraint to prevent updating a column based on its prior valueSQL 约束,以防止根据其先前值更新列
【发布时间】:2013-01-16 20:34:57
【问题描述】:

是否可以使用检查约束(或其他一些技术)来防止在更新记录时设置与其先前值相矛盾的值。

一个例子是 NULL 时间戳,表示发生了一些事情,例如“file_exported”。一旦文件被导出并具有非 NULL 值,就不应再将其设置为 NULL。

另一个例子是命中计数器,其中整数只允许增加,但不能减少。

如果对我使用 postgresql 有帮助,但我希望看到适合任何 SQL 实现的解决方案

【问题讨论】:

  • 到目前为止你有没有研究过什么?
  • 最好为此使用触发器。检查当前值是否不为空,如果他们尝试将其设置为空,则抛出错误。不过,如果您需要历史更改日志,则必须自己构建..
  • 您使用的是哪个 DBMS?甲骨文? PostgreSQL?
  • 一个简单的行触发器ON UPDATE,检查NEW 和OLD 值可以做你想做的事。
  • 不,你不能有一个知道除了行本身之外的任何事情的约束。您可以基于行中的列设置约束,但不能在其他表中,也不能在过去。

标签: sql postgresql check-constraints


【解决方案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 中的存储过程和函数可以访问旧值和新值,并且该代码可以访问任意表和列。在存储过程中构建简单的(粗略的?)有限状态机并不难。你甚至可以用这种方式构建表驱动的状态机。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-27
        • 1970-01-01
        相关资源
        最近更新 更多