我的应用程序涉及使用从表单提交数据(“请求”)到SQL Server 2005数据库,供管理员稍后审核和批准。用户应该有权插入新请求,但无法修改他们已经提交的请求。
使用单个表,这很简单:只授予他们 INSERT权限而没有UPDATE权限。但是请求实际上跨越了两个表,具有一对多的关系。我需要阻止用户为现有请求插入额外的子行。理想情况下,这应该在数据库级别强制执行:允许父行和一个或多个子行插入同一事务,但是一旦提交了该事务,就会阻止插入新的子行那个外键。
实现这一目标的最佳方法是什么?有没有办法在没有触发器的情况下强制执行这种“参照完整性”的特殊风格?如果它们只是触发器,那么我如何测试父行是否已插入当前事务中?
答案 0 :(得分:3)
存储过程或触发器
如果您在子表上授予权限,那么他们就可以编写。
触发器将禁止写入,并且存储过程允许您首先阻止写入,因为只有存储的proc写入表。
没有“本机”引用完整性可以捕获您的业务逻辑,因为它是根据您的情况定制的。
答案 1 :(得分:1)
使用存储过程插入数据:
第一个存储过程插入父行;它会自动添加有关插入用户和设置状态字段的信息,
第二个存储过程在检查其父行后插入子行;如果调用用户无权向给定父行添加项目或父行的状态不允许添加新位置,则会引发错误。
或者,您可以使用触发器进行检查。但这比明确调用存储过程有点棘手。人们有时会忘记触发器。
答案 2 :(得分:1)
以下示例说明了如何使用触发器来实现此行为。请注意,如果在事务内部一次插入一个子行,而不是在单个INSERT
语句中,则这将不起作用。
CREATE TABLE parent1
(id INT PRIMARY KEY)
CREATE TABLE child1
(id INT
,parent_id INT
)
GO
ALTER TABLE child1 ADD CONSTRAINT chilld1fk FOREIGN KEY (parent_id)
REFERENCES parent1 (id)
GO
CREATE TRIGGER trg_child1
ON child1
INSTEAD OF INSERT
AS
SELECT parent_id
FROM child1 AS c
WHERE EXISTS (SELECT 1
FROM inserted AS i
WHERE i.parent_id = c.parent_id
)
IF @@ROWCOUNT > 0
BEGIN
RAISERROR('You cannot amend this request',16,1)
END
ELSE
BEGIN
INSERT child1
SELECT id
,parent_id
FROM inserted
END
GO
BEGIN TRAN
INSERT parent1
VALUES (1)
INSERT child1
(id
,parent_id
)
SELECT 10,1
UNION SELECT 11,1
COMMIT
-- attempting to insert another child outside the transaction
-- will result in an error
INSERT child1
SELECT 12,1
SELECT * FROM child1