0

我的应用程序涉及使用将表单中的数据(“请求”)提交到 SQL Server 2005 数据库,以供主管稍后审查和批准。用户应该有权插入新请求,但不能修改他们已经提交的请求。

对于单个表,这很简单:只授予他们 INSERT 权限而不授予 UPDATE 权限。但是请求实际上跨越了两个表,具有一对多的关系。我需要防止用户为现有请求插入额外的子行。理想情况下,这应该在数据库级别强制执行:允许在同一事务中插入父行和一个或多个子行,但是一旦提交该事务,就禁止使用该外键插入新的子行。

实现这一目标的最佳方法是什么?有没有办法在没有触发器的情况下强制执行这种特殊的“参照完整性”?如果触发器是唯一的方法,那么我如何测试父行是否已插入当前事务中?

4

3 回答 3

3

存储过程或触发器

如果您授予对子表的权限,那么他们将能够写入。

触发器将禁止写入,而存储过程允许您首先阻止写入,因为只有存储过程写入表。

没有“本机”引用完整性可以捕获您的业务逻辑,因为它是根据您的情况定制的。

于 2010-01-21T08:15:14.730 回答
1

使用存储过程插入数据:

  • 第一个存储过程插入父行;它会自动添加有关插入用户的信息并设置状态字段,

  • 第二个存储过程在检查其父行后插入子行;如果调用用户无权将项目添加到给定的父行或父行的状态不允许添加新位置,则会引发错误。

或者,您可以使用触发器进行检查。但这比显式调用存储过程要复杂一些。人们有时往往会忘记触发器。

于 2010-01-21T08:29:14.250 回答
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
于 2010-01-21T08:33:05.123 回答