触发器过程的一致性(行触发器之前)Postgresql

发布于 2024-08-27 17:20:23 字数 1196 浏览 5 评论 0原文

使用 Postgresql。

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

问题是……

“BEFORE INSERT FOR EACH ROW”能否保证每一行依次插入“检查”和“插入”?我是否需要在表上加额外的锁才能避免并发插入?

检查新的 row1 ->插入第1行->检查新的 row2 ->插入 row2

--
--

-- 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

或者也许我应该问, 如何使用触发器来实现“主要”或“唯一”约束式 SQL。

Using Postgresql.

I try to use TRIGGER procedure to make some consistency check on INSERT.

The question is ......

whether "BEFORE INSERT FOR EACH ROW" can make sure each row to insert "checked" and "inserted" one after another? do I need extra lock on table to survive from concurrent insert?

check for new row1 -> insert row1 -> check for new row2 -> insert row2

--
--

-- 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!

this is OK

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

This is not OK

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

or maybe I should ask,
how can I use Trigger to implement "Primary" or "Unique" constraint-like SQL.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

暮色兮凉城 2024-09-03 17:20:23

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

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

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

编辑添加

要使用触发器获得类似(或更复杂)的事务安全行为,您可以创建一个 CONSTRAINT 触发器,推迟到事务提交时间。这些触发器函数需要是 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();

Your example can be done with a unique index:

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

This will result in a statement within a second transaction that would that could inviolate the constraint, blocking till the first transaction commits or rolls back.

Edited to add:

To get similar (or more complex) transactionally safe behaviour with triggers, you can create a CONSTRAINT trigger, that is deferred till transaction commit time. These trigger functions need to be AFTER triggers, checking whether your constraint has been violated:

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();
碍人泪离人颜 2024-09-03 17:20:23

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

Why can't you use a unique key to enforce this?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文