替代检查视图中的约束

时间:2012-03-17 15:15:28

标签: sql sql-server sql-server-2008

所以我有两张桌子:

Requests
--------
Id
RequestSchemeId
ReceivedByUserId

ForwardedRequests
-----------------
Id
RequestId (FK to Id column of Requests Table)
ForwardedToUserId

和一个视图

ForwardedRequestsInRequestSchemes
---------------------------------
Requests.RequestSchemeId
Requests.ReceivedByUserId
ForwardedRequests.ForwardedToUserId

在视图中向Requests.ReceivedByUserId != ForwardedRequests.ForwardedToUserId添加约束等效的标准/推荐方法是什么?

我知道视图中不允许检查约束。使用SQL Server 2008。

编辑:

这是this question的后续问题。

业务规则:

  1. 可以将同一请求转发给多个用户。因此ForwardedRequests表中的Id列。

  2. 用户只能收到特定RequestScheme的一个请求。所以我在Requests表中为RequestSchemeId + ReceivedByUserId创建了一个UniqueKey约束。

  3. 只有当转发用户在任何其他用户的同一方案下没有转发请求时,才能将请求转发给其他用户。正如Martin在linked question中建议的那样,我从两个表创建了一个视图,并在Requests.RequestSchemeId + ForwardedRequests.ForwardedToUserId上添加了一个唯一约束。

  4. 这个问题涉及的业务规则是,请求的接收者不能将其转发给他/她自己。

2 个答案:

答案 0 :(得分:9)

我可以想到让SQL Server为您强制执行此操作的几种方法。虽然对看到任何其他方法感兴趣,但两者都非常讨厌。

1)您可以在索引视图ForwardedRequestsInRequestSchemes中添加一个额外的列1/(ForwardedToUserId - ReceivedByUserId) AS FailIfSame,如果这两个值相同,则会引发Divide by zero error。这确实意味着您最终会在索引视图中存储冗余列。

2)您可以创建一个新视图,将任何此类行交叉连接到两行表上,然后在该视图上定义唯一约束。此视图将始终为空。

CREATE TABLE dbo.TwoRows(C INT) INSERT INTO dbo.TwoRows VALUES(1),(1)

GO

CREATE VIEW dbo.FailIfForwardedUserEqualToReceivedByUser
WITH SCHEMABINDING 
AS
  SELECT 1 AS C
  FROM   dbo.ForwardedRequests FR 
         INNER JOIN dbo.Requests R 
           ON R.Id = FR.RequestId AND R.ReceivedByUserId = FR.ForwardedToUserId
         CROSS JOIN dbo.TwoRows

GO

CREATE UNIQUE CLUSTERED INDEX ix ON 
     dbo.FailIfForwardedUserEqualToReceivedByUser(C)

答案 1 :(得分:2)

一种方法是禁止表上的update, insert, delete权限,并使用存储过程强制执行业务需求。例如,

create procedure dbo.AddRequestForward(
    @requestId int
,   @forwardedToUserId int)
as
insert  ForwardedRequests
        (ForwardedRequests, ForwardedRequests)
select  @requestId
,       @forwardedToUserId
where   not exists
        (
        select  *
        from    Requests
        where   Id = @requestId
                and @forwardedToUserId = @forwardedToUserId
        )

if @@rowcount = 0
    return -1 -- Forwarded and Received identical user
return 1 -- Success
go