如果行显示在列表中但不在表中,并且如果它们没有出现在列表中但是在表中,则如何插入行?

时间:2013-11-17 00:16:11

标签: c# sql sql-server sql-server-2008-r2 relational-database

最近,我负责清理SQL Server 2008数据库和网站代码,并且无法提出解决以下问题的优雅解决方案。在网站上,有些用户具有不同的角色,每个角色都有许多权限。网站上的管理员可以启用或禁用给定角色的权限。

目前,数据库未规范化,表格设置如下:

角色:

RoleID | RoleName | Permissions
  1    | Admin    | 1;2;3;4;5;etc.
  2    | User     | 4;23;54;etc.
  3    | Person   | 54;2;3;45;etc.

权限:

PermissionID | PermissionName
    1        | Random Name
    2        | You get the idea
    3        | etc.

要更新角色权限,它们基本上遍历代码隐藏中的每个复选框,生成“权限字符串”并在单个调用中更新行。

我想按如下方式规范化数据库:

角色:

RoleID | RoleName 
  1    | Admin    
  2    | User     
  3    | Person  

权限:

PermissionID | PermissionName
    1        | Random Name
    2        | You get the idea
    3        | etc.

RolePermissions:

rpID | RoleID | PermissionID
 1   |   1    |      1
 2   |   1    |      2
 3   |   1    |      20
 4   |   2    |      5
 5   |   2    |      2

问题是用户可能希望一次更改20-30个权限。我能想到的唯一方法是删除该角色的每个权限,然后逐个添加每个角色权限关系。通常情况下这不是问题,但我们的数据库存储在与我们的代码不同的服务器上,因此单独制作30多个命令中的每一个的网络延迟将是一个杀手。我也知道在这种情况下,用户应该很少更新角色,但我试图将整个数据库切换为规范化,因此在更常用的页面上的其他地方会出现此问题。

我想知道在一个或两个SQL命令中是否有一种优雅的方式将新的Role-Permission关系插入RolePermissions表(如果它们不存在),并删除任何不再存在的RolePermissions。这背后的代码是C#,因此我可以非常轻松地生成所需的任何SQL命令字符串。有什么建议吗?

如果需要更多信息,我会提供。

1 个答案:

答案 0 :(得分:1)

如果您使用的是最近支持表值参数的客户端库,则可以使用它们将数据列表发送到存储过程。

create type PermissionList as table (
    PermissionID int not null
);
Go

create proc UpdateRolePermissions
    @roleID int, 
    @permissionList PermissionList readonly
as
with t as (
    select
        *
    from
        dbo.RolePermissions
    where
        RoleId = @roleID
)
merge
    t
using
    @permissionList s
on
    t.PermissionID = s.PermissionID
when not matched by source then 
    delete
when not matched by target then
    insert (roleID, permissionID)
    values (@roleID, s.PermissionID)
;
go

如果有更多数据且您需要在匹配时更新某些内容,您还可以添加when matched then update子句

Example of using table valued parameter from C#

<强> Example SQLFiddle