【问题标题】:consistency of Trigger Procedure (before row trigger) Postgresql触发器过程的一致性(行触发器之前)Postgresql
【发布时间】:2010-04-06 18:23:21
【问题描述】:

使用 Postgresql。

我尝试使用 TRIGGER 过程对 INSERT 进行一些一致性检查。

问题是……

“BEFORE INSERT FOR EACH ROW”能否保证每一行都依次插入“checked”和“inserted”?我是否需要额外的表锁定才能从并发插入中生存?

检查新的第1行->插入第1行->检查新的第2行->插入第2行

--
--

-- unexpired product name is unique.
CREATE TABLE product (
  "name"    VARCHAR(100) NOT NULL,
  "expired" BOOLEAN      NOT NULL
);

CREATE OR REPLACE FUNCTION check_consistency() RETURNS TRIGGER AS $$
  BEGIN
    IF EXISTS (SELECT * FROM product WHERE name=NEW.name AND expired='false') THEN
      RAISE EXCEPTION 'duplicated!!!';              
    END IF;
    RETURN NEW;
  END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_check_consistency
BEFORE INSERT ON product
  FOR EACH ROW EXECUTE PROCEDURE check_consistency();

--
INSERT INTO product VALUES("prod1", true);
INSERT INTO product VALUES("prod1", false);
INSERT INTO product VALUES("prod1", false); // exception!

没关系

 name | expired
 ==============
 p1   |  true
 p1   |  true
 p1   |  false

这样不行

 name | expired
 ==============
 p1   |  true
 p1   |  false
 p1   |  false

或者我应该问, 如何使用 Trigger 来实现类似“Primary”或“Unique”约束的 SQL。

【问题讨论】:

  • 不要使用触发器,太慢且不可靠:许多事务在 SELECT 中可以看到相同的结果,因此所有事务都会正常,并且您的数据不会(部分)唯一。

标签: postgresql stored-procedures triggers


【解决方案1】:

您的示例可以使用唯一索引来完成:

CREATE UNIQUE INDEX uq_check_consistency ON product ( name ) WHERE NOT expired;

这将导致第二个事务中的语句可能违反约束,阻塞直到第一个事务提交或回滚。

编辑添加

要使用触发器获得类似(或更复杂)的事务安全行为,您可以创建一个CONSTRAINT trigger,它会延迟到事务提交时间。这些触发器函数需要AFTER触发器,检查你的约束是否被违反:

CREATE OR REPLACE FUNCTION after_check_consistency() RETURNS TRIGGER AS $$
  BEGIN
    IF (SELECT count(*) FROM product WHERE name=NEW.name AND expired='false') > 1 THEN
      RAISE EXCEPTION 'duplicated!!!';              
    END IF;
    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;


CREATE CONSTRAINT TRIGGER trigger_check_consistency
AFTER INSERT OR UPDATE ON product
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW EXECUTE PROCEDURE after_check_consistency();

【讨论】:

    【解决方案2】:

    为什么不能使用唯一键来强制执行此操作?

    【讨论】:

    • 我只想对某些特定的列值应用唯一约束。以上面的代码为例。同一个商品名如果都过期了可以多次存在,但是expired=false的商品名必须唯一。
    • 您可以将唯一索引创建为部分索引,如 Stephan Denne 所示。见postgresql.org/docs/8.4/interactive/indexes-partial.html
    • 谢谢,部分索引正是解决方案!!!但是我还是不知道Trigger "FOR EACH ROW" 能不能保证一个顺序的UPDATE或者INSERT,这样并发表INSERT或者UPDATE就不会违反约束了。
    猜你喜欢
    • 2011-12-19
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    • 2012-01-05
    • 2011-03-15
    相关资源
    最近更新 更多