我想将父实体的子实体的某个属性的总和限制为该父实体的某个属性。我想使用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] 删除。
我来说两句