Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 触发器以确保聚合和小于其他表中的值_Sql_Postgresql_Database Design_Triggers_Sql Insert - Fatal编程技术网

Sql 触发器以确保聚合和小于其他表中的值

Sql 触发器以确保聚合和小于其他表中的值,sql,postgresql,database-design,triggers,sql-insert,Sql,Postgresql,Database Design,Triggers,Sql Insert,我工作的问题领域是电子商务的退货管理 我正在使用Postgres(11.9)并有以下表格(我已经从每个表格中删除了一些与问题无关的字段): 创建表“订单”( id BIGSERIAL主键, 平台文本不为空, 平台订单id文本不为空, 约束平台\订单\ id \唯一唯一(平台,平台\订单\ id) ); 创建表格订单项目( id BIGSERIAL主键, 订单id int8不为空, 平台项目id文本不为空, 数量整数, 约束FK\订单\项目\订单\ id外键(订单\ id)引用“订单”, 约束平台

我工作的问题领域是电子商务的退货管理

我正在使用Postgres(11.9)并有以下表格(我已经从每个表格中删除了一些与问题无关的字段):

创建表“订单”(
id BIGSERIAL主键,
平台文本不为空,
平台订单id文本不为空,
约束平台\订单\ id \唯一唯一(平台,平台\订单\ id)
);
创建表格订单项目(
id BIGSERIAL主键,
订单id int8不为空,
平台项目id文本不为空,
数量整数,
约束FK\订单\项目\订单\ id外键(订单\ id)引用“订单”,
约束平台项目id唯一(订单id、平台项目id)
);
创建表返回(
id BIGSERIAL主键,
订单id int8不为空,
约束FK\ U返回\订单\ id外键(订单\ id)引用“订单”
);
创建表返回项(
return_id int8不为空,
订单项目id int8不为空,
数量整数不为空,
约束FK_return_item_return_id外键(return_id)引用return,
约束FK\ U返回\项目\项目\ id外键(订单\项目\ id)引用订单\项目
);
为了简单地解释这个领域,我从电子商务平台提取订单,并将它们存储在我的数据库中。订单由一个或多个具有
数量>1
的不同项目组成。当用户希望退货时,他们最多可退货至每次退货的数量

更具体地说,如果我一次订购两件黑色小t恤,你会在数据库中找到一个
订单
,其中有一个
订单项目
,数量为
2
。我将能够创建两个单独的退货,每个退货都有一个
退货项目
引用相同的
订单项目
,但数量为1

order\u item
return\u item
插入到不同的事务中,我不会阻止多个事务同时更新其中任何一个

如何确保具有特定
订单项目id
的所有
退货项目
的每个
数量的总和不超过具有所述
id
的相应
订单项目中存储的数量

用更简单的英语来说,当第三件商品在原始订单中的数量为2时,如我所描述的示例中所示,如何防止第三件商品被退回


在大多数情况下,编写应用程序检查以捕获这一点非常容易,而且在我的
返回项插入中添加业务规则检查
WHERE
子句也不难,但这两种解决方案都没有为我提供唯一性约束所能提供的一致性保证。我该如何在此处插入错误时编写触发器?或者有比触发器更好的方法吗?

我能想到的唯一解决方案是反规范化

订单项目
中添加
整数
total\u returns
,每当添加或删除行或
数量
更改时,该列由
return\u项目
上的触发器修改

然后,您可以对
order\u项
进行一个简单的检查约束,以确保您的不变量保持不变

一些示例代码:
开始;
/*为了一致性*/
更改表顺序\u项
更改数量设置不为空
改变数量设置默认为0;
更改表顺序\u项
ADD total_返回bigint默认值0不为空;
更改表顺序\u项
添加约束不\u太多\u返回

CHECK(total_returns我不建议存储这些派生信息,因为维护起来很繁琐

替代,您可以在DML查询中实现逻辑。请考虑下面的查询来登记新的返回项:

insert into return_item (order_item_id, quantity)
select v.*
from (values ($1, $2)) as v(order_item_id, quantity)
inner join order_items oi on oi.id = v.order_item_id
where oi.quantity >= v.quantity + (
    select coalesce(sum(ri.quantity), 0) from return_item ri where ri.order_item_id = v.order_item_id
)
输入值以
$1
$2
的形式给出。查询将在
订单\项目
中显示相应的行,并检查该项目的总退货数量是否大于订单金额

整个逻辑是在一个查询中实现的,因此如果您有多个并发进程,就不会有竞争条件的风险

从应用程序中,您可以检查查询是否影响了任何行。如果没有影响,则您知道返回被拒绝


如果要定期使用存储过程,可以将查询放入存储过程中。

您特别要求使用触发器解决方案。对于记录,您也可以使用纯SQL实现同样的效果,只要您可以确保所有客户端都使用必要的语句。相关示例:

触发溶液 您提到,并发写访问是可能的。这使得它更加复杂。例如,两个事务可能试图同时从相同的
order\u项目
返回一个项目。这两个事务都检查并发现可以再返回一个项目,从而超过
order\u项目数量
经典并发警告

为了防止这种情况,您可以使用事务隔离,但这要昂贵得多,所有可能写入相关表的事务都必须遵守它

或者,在默认隔离级别中删除strategic。以下是一个基本实现:

触发功能:

创建函数trg\u return\u item\u insup\u bef()
返回触发器
语言plpgsql AS
$func$
声明
_订购的物品;
_剩余的_项目;
开始
选择数量
从订单项目
其中id=NEW.order\u item\u id
对于无密钥更新--首先锁定父行…(!!!)
在获取数量时,放入已订购的项目
选择已订购的项目-合并(总和(数量),0)
从退货项目
其中order\u item\u id=NEW.order\u item\u id
进入剩余的项目;
如果是新的,quanti
insert into return_item (order_item_id, quantity)
select v.*
from (values ($1, $2)) as v(order_item_id, quantity)
inner join order_items oi on oi.id = v.order_item_id
where oi.quantity >= v.quantity + (
    select coalesce(sum(ri.quantity), 0) from return_item ri where ri.order_item_id = v.order_item_id
)