创建无触发器的数据库约束:防止交易所有工单被全部VOID
解决方案:无触发器实现禁止交易所有工单设为VOID的约束
原方案无效原因
你的自定义函数+CHECK约束方案不生效,核心问题是SQL Server的CHECK约束是行级验证机制:
- 它仅针对当前被插入/更新的单行数据验证条件,不会主动扫描整张表校验同交易下其他工单的状态。
- 当批量更新某交易的所有工单为VOID,或逐行更新最后一个非VOID工单时,CHECK约束不会触发对整个交易工单集合的状态检查,导致约束失效。
- 另外,函数中关联
Transaction表完全多余,直接查询Work_Order表即可获取统计结果。
针对「每个交易对应1个工单」的最优方案
结合你的实际业务规则(1交易=1工单),可以通过两步约束彻底解决问题:
1. 保证交易与工单的一一对应
添加唯一约束,确保每个Transaction_ID在Work_Order表中仅出现一次:
ALTER TABLE Work_Order ADD CONSTRAINT UQ_Work_Order_Transaction_ID UNIQUE (Transaction_ID);
2. 禁止唯一工单被设为VOID
直接添加CHECK约束,限制该工单的VOID字段不能为1:
ALTER TABLE Work_Order ADD CONSTRAINT CK_Work_Order_Cannot_Void_Single_Order CHECK (VOID = 0);
这样既保证了交易与工单的一对一关系,又从根本上避免了唯一工单被误设为VOID的问题。
通用方案(支持1交易对应多工单场景)
如果后续业务允许一个交易对应多个工单,但仍需保证至少保留一个非VOID工单,可以通过索引视图+CHECK约束实现表级验证(无需触发器):
1. 创建索引视图,识别所有工单全为VOID的交易
CREATE VIEW vw_Work_Order_All_Void WITH SCHEMABINDING AS SELECT wo.Transaction_ID FROM dbo.Work_Order wo GROUP BY wo.Transaction_ID -- 筛选出所有工单都被标记为VOID的交易 HAVING COUNT_BIG(*) = COUNT_BIG(CASE WHEN VOID = 1 THEN 1 END);
2. 为视图创建唯一聚集索引
CREATE UNIQUE CLUSTERED INDEX IX_vw_Work_Order_All_Void ON vw_Work_Order_All_Void (Transaction_ID);
3. 添加约束禁止视图存在数据
ALTER TABLE vw_Work_Order_All_Void ADD CONSTRAINT CK_Work_Order_No_All_Void CHECK (1 = 0);
当某个交易的所有工单被设为VOID时,视图会生成对应行,此时1=0的约束会触发错误,阻止该操作执行。
内容的提问来源于stack exchange,提问作者Mark H
相关产品推荐
相关产品推荐

