postgres-列总和的约束(无触发器)

梅塔吉策

我想将父实体的子实体的某个属性的总和限制为该父实体的某个属性。我想使用PostgreSQL而不使用触发器来执行此操作。下面是一个例子;

假设我们有一个带有volume属性的箱子。我们要用较小的盒子填充它,它们具有自己的体积属性。板条箱中所有盒子的体积总和不能大于板条箱的体积。

我想到的想法是这样的:

CREATE TABLE crates (
  crate_id int NOT NULL,
  crate_volume int NOT NULL,
  crate_volume_used int NOT NULL DEFAULT 0,

  CONSTRAINT crates_pkey PRIMARY KEY (crate_id),

  CONSTRAINT ukey_for_fkey_ref_from_boxes 
    UNIQUE (crate_id, crate_volume, crate_volume_used),

  CONSTRAINT crate_volume_used_cannot_be_greater_than_crate_volume 
    CHECK (crate_volume_used <= crate_volume),

  CONSTRAINT crate_volume_must_be_positive CHECK (crate_volume >= 0)
);



CREATE TABLE boxes (
  box_id int NOT NULL,
  box_volume int NOT NULL,

  crate_id int NOT NULL,
  crate_volume int NOT NULL,
  crate_volume_used int NOT NULL,

  id_of_previous_box int,
  previous_sum_of_volumes_of_boxes int,
  current_sum_of_volumes_of_boxes int NOT NULL,

  id_of_next_box int,

  CONSTRAINT boxes_pkey PRIMARY KEY (box_id),

  CONSTRAINT box_volume_must_be_positive CHECK (box_volume >= 0),

  CONSTRAINT crate_fkey FOREIGN KEY (crate_id, crate_volume, crate_volume_used) 
    REFERENCES crates (crate_id, crate_volume, crate_volume_used) MATCH SIMPLE
    ON UPDATE CASCADE ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,

  CONSTRAINT previous_box_self_ref_fkey FOREIGN KEY (id_of_previous_box, previous_sum_of_volumes_of_boxes) 
    REFERENCES boxes (box_id, current_sum_of_volumes_of_boxes) MATCH SIMPLE
    ON UPDATE CASCADE ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT ukey_for_previous_box_self_ref_fkey UNIQUE (box_id, current_sum_of_volumes_of_boxes),
  CONSTRAINT previous_box_self_ref_fkey_validity UNIQUE (crate_id, id_of_previous_box),

  CONSTRAINT next_box_self_ref_fkey FOREIGN KEY (id_of_next_box) 
    REFERENCES boxes (box_id) MATCH SIMPLE
    ON UPDATE CASCADE ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT next_box_self_ref_fkey_validity UNIQUE (crate_id, id_of_next_box),

  CONSTRAINT self_ref_key_integrity CHECK (
    (id_of_previous_box IS NULL AND previous_sum_of_volumes_of_boxes IS NULL) OR
    (id_of_previous_box IS NOT NULL AND previous_sum_of_volumes_of_boxes IS NOT NULL)
  ),

  CONSTRAINT sum_of_volumes_of_boxes_check1 CHECK (current_sum_of_volumes_of_boxes <= crate_volume),
  CONSTRAINT sum_of_volumes_of_boxes_check2 CHECK (
    (previous_sum_of_volumes_of_boxes IS NULL AND current_sum_of_volumes_of_boxes=box_volume) OR
    (previous_sum_of_volumes_of_boxes IS NOT NULL AND current_sum_of_volumes_of_boxes=box_volume+previous_sum_of_volumes_of_boxes)
  ),

  CONSTRAINT crate_volume_used_check CHECK (
    (id_of_next_box IS NULL AND crate_volume_used=current_sum_of_volumes_of_boxes) OR
    (id_of_next_box IS NOT NULL)
  )
);

CREATE UNIQUE INDEX single_first_box ON boxes (crate_id) WHERE id_of_previous_box IS NULL;
CREATE UNIQUE INDEX single_last_box ON boxes (crate_id) WHERE id_of_next_box IS NULL;

我的问题是这是否是一种方法,以及是否有更好的方法(减少混乱,更优化等)。还是我应该坚持使用触发器?

提前致谢。

丹尼斯·德·伯纳迪

我的问题是,是否有更好的方法(减少混乱,更优化等)。

是的,这里有:一句话,使用触发器...

不,没关系,您不想使用一个。在这里使用触发器;没有,没有。

扩展我和其他人先前发表的评论:

您正在做的事情就是编写一个约束触发器来验证sum(boxes.volume) <= crate.volume它只是以非常非常卑鄙的方式(通过将检查约束以及唯一键和外键伪装成聚合函数)来进行此操作,然后在您的应用程序中进行相关计算。

避免使用真正的触发器的唯一成就就是当两个并发更新尝试影响同一条板条箱时会出现错误。所有这些都以维护不必要的唯一索引和外键为代价。

当然,您将最终解决一些或所有这些问题,并通过使外键可延期,添加锁yada yada来进一步完善“实现”。但是最后,您基本上要做的就是编写一个效率极低的聚合函数。

因此,使用触发器。可以使用箱上的after触发器在板条箱中维护current_volume列,并使用对板条箱的简单check()约束强制执行检查。或在框上添加约束触发器,以直接执行检查。

如果您需要更多说服力,则只需考虑要创建的开销。真。冷静地思考一下:您将维护不少于六个字段,这些字段绝对不会超出您的约束,而不会使用触发器在板条箱中维护一个卷列(如果有的话),还有许多无用的唯一索引和与它们相关的外键约束,当我尝试枚举它们时,我确实会失去计数。然后检查它们的约束。这些东西在存储和写入性能方面加起来。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

postgres-列总和的约束(无触发器)

来自分类Dev

在postgres更新触发器中检测列更改

来自分类Dev

Postgres触发器不更新指定的列

来自分类Dev

触发器中的Postgres未定义列

来自分类Dev

创建或替换触发器postgres

来自分类Dev

Postgres触发器的now()时差很奇怪

来自分类Dev

在postgres中创建触发器

来自分类Dev

创建Postgres规则或触发器以在插入时自动将列转换为小写或大写

来自分类Dev

如何使用Postgres触发器双重写入一个表中的两列?

来自分类Dev

Postgres触发器中的远程异常处理?

来自分类Dev

如何在Postgres中使用“ for语句”触发器?

来自分类Dev

Postgres触发器-使用2个不同的表

来自分类Dev

在AWS-RDS中创建Postgres事件触发器

来自分类Dev

Postgres触发器:将旧记录写入新表

来自分类Dev

如何判断是否/何时执行Postgres触发器

来自分类Dev

Postgres触发器插入所有行

来自分类Dev

postgres 9.3数据库触发器创建

来自分类Dev

Heroku Postgres删除触发器未通过Heroku Connect复制到Salesforce

来自分类Dev

在postgres中的间隔时间内运行触发器

来自分类Dev

Postgres错误关系“新”在插入触发器上不存在

来自分类Dev

在插入触发器Postgres上从其他表复制值

来自分类Dev

Postgres查找在DML触发器中修改了哪些表

来自分类Dev

插入触发器上不存在Postgres错误关系“ new”

来自分类Dev

如何在Postgres事件触发器中获取更改后的表的名称?

来自分类Dev

Heroku Postgres删除触发器未通过Heroku Connect复制到Salesforce

来自分类Dev

在整个事务而不是每个更新操作上执行Postgres触发器

来自分类Dev

Postgres触发器会在任何情况下为修改设置值吗?

来自分类Dev

在 postgres 中的临时表中的触发器上插入值

来自分类Dev

Postgres 触发器和函数来自动完成表格

Related 相关文章

  1. 1

    postgres-列总和的约束(无触发器)

  2. 2

    在postgres更新触发器中检测列更改

  3. 3

    Postgres触发器不更新指定的列

  4. 4

    触发器中的Postgres未定义列

  5. 5

    创建或替换触发器postgres

  6. 6

    Postgres触发器的now()时差很奇怪

  7. 7

    在postgres中创建触发器

  8. 8

    创建Postgres规则或触发器以在插入时自动将列转换为小写或大写

  9. 9

    如何使用Postgres触发器双重写入一个表中的两列?

  10. 10

    Postgres触发器中的远程异常处理?

  11. 11

    如何在Postgres中使用“ for语句”触发器?

  12. 12

    Postgres触发器-使用2个不同的表

  13. 13

    在AWS-RDS中创建Postgres事件触发器

  14. 14

    Postgres触发器:将旧记录写入新表

  15. 15

    如何判断是否/何时执行Postgres触发器

  16. 16

    Postgres触发器插入所有行

  17. 17

    postgres 9.3数据库触发器创建

  18. 18

    Heroku Postgres删除触发器未通过Heroku Connect复制到Salesforce

  19. 19

    在postgres中的间隔时间内运行触发器

  20. 20

    Postgres错误关系“新”在插入触发器上不存在

  21. 21

    在插入触发器Postgres上从其他表复制值

  22. 22

    Postgres查找在DML触发器中修改了哪些表

  23. 23

    插入触发器上不存在Postgres错误关系“ new”

  24. 24

    如何在Postgres事件触发器中获取更改后的表的名称?

  25. 25

    Heroku Postgres删除触发器未通过Heroku Connect复制到Salesforce

  26. 26

    在整个事务而不是每个更新操作上执行Postgres触发器

  27. 27

    Postgres触发器会在任何情况下为修改设置值吗?

  28. 28

    在 postgres 中的临时表中的触发器上插入值

  29. 29

    Postgres 触发器和函数来自动完成表格

热门标签

归档