我有一个如下表:
the_table
+----+----------+-------+
| id | category | total |
+----+----------+-------+
| 1 | A | 0 |
| 2 | B | 5 |
| 3 | A | 7 |
| 4 | A | 2 |
+----+----------+-------+
我想插入一个新行,其中total
列将更新为previous_total
+ new_total
,以保持连续的总和对于每个类别。
示例:INSERT INTO the_table VALUES (DEFAULT, 'A', most_recently_entered_A - 1)
[问题是,如果most_recently_entered_A - n < 0
(即n
是一个使总数为负的数字,我希望插入失败。
为了执行此操作,我需要确保没有其他插入具有先前的总值,因为这显然会导致总条件可能不正确的竞争状况。
我假设有某种触发器或函数可以使我在锁定特定total
的最后一个category
的同时执行此插入操作,但老实说,我不知道这样做的安全方法。
类似以下工作吗?
DO $$
DECLARE
previous INTEGER;
new_val INTEGER;
cat TEXT;
BEGIN
new_val := 2;
cat := 'A';
SELECT total FROM the_table WHERE category = cat ORDER BY id DESC LIMIT 1 INTO previous;
IF previous - new_val < 0 THEN
RAISE EXCEPTION 'The total will be negative if this insert is performed';
ELSE
INSERT INTO the_table VALUES (default, cat, previous - new_val);
END IF;
END $$;
以上内容将保留不变式,并且有更高效的方法吗?
编辑:我意识到我忘记了重要的一点。我没有在total
列上使用检查约束的原因是因为此约束仅适用于某些类别。例如,我可以添加另一个类别C
,该类别的总和为负。
[如果您希望在插入总数变为负数时插入失败,请使用check
约束:
alter table the_table add constraint chk_the_table_total
check (total >= 0);
请勿尝试在您的代码中执行此操作。让数据库来做保证完整性的工作。