【问题标题】:How do I insert rows if their values appear in a list but are not in the table and delete them if they do not appear in the list but are in the table?如果它们的值出现在列表中但不在表中,我如何插入行,如果它们没有出现在列表中但在表中,如何删除它们?
【发布时间】:2013-11-17 00:16:10
【问题描述】:

最近,我负责清理 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.

角色权限:

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 命令字符串。有什么建议吗?

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

【问题讨论】:

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


    【解决方案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

    【讨论】:

    • SQLFiddle 完美地完成了我想要的。谢谢你。下周我将在我们的数据库上试用它,但看起来应该可以完成这项工作。现在最难的部分是找出代码的 C# 部分,但是由于您提供的链接,这应该不会太难。如果我能赞成你的回答,我会的。非常感谢!
    • 巧妙的技巧@Laurence 在目标表的角色中使用 CTE。它必须被劫持;)+1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多