介绍 (Introduction)

Let’s say we have a database role that we don’t need anymore because we defined another security policy using another database role. What are the steps to follow in order to properly drop this database role? That’s the question this article will try to answer, covering as many cases as possible.

假设我们有了一个不再需要的数据库角色,因为我们使用另一个数据库角色定义了另一个安全策略。 要正确删除此数据库角色,应执行哪些步骤? 这是本文将尝试回答的问题,涉及尽可能多的情况。

In the following article, we will consider the simple steps we can follow in order to do this task using both SSMS and T-SQL. Then, we will focus on some facts that will lead us to the conclusion that, if we do it this way, it won’t work every time. We will list some situations where it could fail and define a test case situation in order to create a stored procedure that will do the job correctly, in all cases bymanaging these situations.

在下一篇文章中,我们将考虑为了使用SSMS和T-SQL来完成此任务而可以遵循的简单步骤。 然后,我们将集中在一些事实上,这些事实将导致我们得出这样的结论:如果以此方式进行操作,那么它就不会每次都起作用。 我们将列出一些可能失败的情况,并定义一个测试用例情况,以便创建一个在所有情况下都可以通过管理这些情况来正确完成工作的存储过程。

通用数据库角色删除过程 (Common database role removal process)

In this section, we will see how to drop a Database Role that does not have any members or any permissions assigned. Furthermore, it does not own any database object (schema, etc.).

在本节中,我们将看到如何删除没有分配任何成员或任何权限的数据库角色。 此外,它不拥有任何数据库对象(模式等)。

How to drop a Database Role using SSMS?

如何使用SSMS删除数据库角色?

Using SQL Server Management Studio (SSMS), we can drop a database role following these steps:

使用SQL Server Management Studio(SSMS),我们可以按照以下步骤删除数据库角色:

  1. Open SSMS and connect to the SQL Server instance 打开SSMS并连接到SQL Server实例
  2. Extend “Databases” node and the one corresponding to the database in which there is a role that has to be dropped. 扩展“数据库”节点和对应于其中必须删除角色的数据库的节点。
  3. Continue to go down in tree view hierarchy by extending “Security” then “Roles” and “Database Roles” nodes.

    通过依次扩展“安全性”,“角色”和“数据库角色”节点,继续深入树状视图层次结构。

  4. Right-click on the Database Role you want to delete and click on “Delete”.

    右键单击要删除的数据库角色,然后单击“删除”。

  5. A dialog will appear and we just need to click on “OK” button.

    将出现一个对话框,我们只需要单击“确定”按钮。

And that’s it.

就是这样。

How to drop a Database Role using T-SQL

如何使用T-SQL删除数据库角色

We just need to run following statement to drop a Database Role called [ToBeDropped].

我们只需要运行以下语句来删除名为[ToBeDropped]的数据库角色。


USE TestDb;
GODROP ROLE [ToBeDropped];
GO

处理可能的删除问题 (Handling possible removal issues)

Database roles are also database principals (like database users). This means that a database role can own objects. It can also be a member of one or more other database roles. In addition, database roles have permissions assigned to them and eventually role members that inherit those permissions.

数据库角色也是数据库主体(例如数据库用户)。 这意味着数据库角色可以拥有对象。 它也可以是一个或多个其他数据库角色的成员。 此外,数据库角色具有分配给他们的权限,并且最终分配了继承这些权限的角色成员。

To sum up, in real life, it’s not always just a matter of firing a DROP ROLE command. We can readily confirm that because even SSMS does not simply run this command when we use it!

综上所述,在现实生活中,不仅仅是触发DROP ROLE命令的问题。 我们可以很容易地确认这一点,因为即使SSMS在使用它时也不会简单地运行此命令!

In fact, if we generate the T-SQL statement that will drop the [ToBeDropped] database role, we may be surprised of the results.

实际上,如果我们生成将删除[ToBeDropped]数据库角色的T-SQL语句,我们可能会对结果感到惊讶。

To generate this statement, right click on the role to be dropped, then go over “Script Database Role as” then “Drop To” and select a destination for the generated script.

要生成此语句,请右键单击要删除的角色,然后依次选择“脚本数据库角色为”和“拖放到”,然后为生成的脚本选择目标。

Here is the script that is generated using SSMS v17.1:

这是使用SSMS v17.1生成的脚本:

There are several noticeable facts that we can notice from this generated script:

我们可以从生成的脚本中注意到几个值得注意的事实:

  • It will check if the role is defined by Microsoft and we could expect a modified version of this script to fail whenever the database role is fixed (although there is no explicit RAISERROR instruction). Actually, here is the message we’d get if we try to drop [db_backupoperator] database role:

    它将检查角色是否由Microsoft定义,并且只要数据库角色已修复(尽管没有明确的RAISERROR指令),我们可以预期该脚本的修改版本将失败。 实际上,如果尝试删除[db_backupoperator]数据库角色,则会收到以下消息:

  • It will list out database principals that are a member of this role and remove their membership to the database role we want to drop. 它将列出该角色成员的数据库主体,并将其成员身份删除为我们要删除的数据库角色。
  • Finally, it will drop the database role. This could be seen as a bug but it will generate an IF EXISTS argument even when the instance version is below 2016.
  • 最后,它将删除数据库角色。 这可能被视为一个错误,但是即使实例版本低于2016,它也会生成IF EXISTS参数。

We can conclude that SSMS developers did think of database role removal as a complex task. But what if we want to drop a role that owns a database schema?

我们可以得出结论,SSMS开发人员确实认为删除数据库角色是一项复杂的任务。 但是,如果我们要删除拥有数据库模式的角色该怎么办?

Let’s create this situation using following statements:

让我们使用以下语句创建这种情况:


USE [TestDb];
GO
CREATE ROLE [RoleToBeDropped];
GO
CREATE SCHEMA [SchemaOwnedByRoleToBeDropped] AUTHORIZATION [RoleToBeDropped];
GO

Now, let’s try to drop [RoleToBeDropped] database role.

现在,让我们尝试删除[RoleToBeDropped]数据库角色。


USE [TestDb] ;
GO
DROP ROLE [RoleToBeDropped] ;
GO

Actually, it won’t work and we’ll get following error message:

实际上,它将无法正常工作,我们将收到以下错误消息:

So, there is more work to do and we’ll try to cover more cases that SSMS does by creating a new stored procedure that will manage the removal of a Database Role.

因此,还有更多工作要做,我们将通过创建一个新的存储过程来管理SSMS的更多情况,该过程将管理数据库角色的删除。

Actually, there other cases where a DROP ROLE statement will fail and some of them were covered for Database User removal in an article entitled “How to drop a SQL Server Login and all its dependencies“. The case includes:

实际上,在其他情况下, DROP ROLE语句将失败,并且在标题为“ 如何删除SQL Server登录名及其所有依赖项 ”的文章中介绍了其中的一些内容,以便删除数据库用户。 该案包括:

  1. Role as Schema Owner 充当架构所有者
  2. Role as Permission grantor (revokes assigned permissions) 充当权限授予者(撤销分配的权限)
  3. Role as Role owner (for another database role in the same database) 作为角色所有者的角色(对于同一数据库中的另一个数据库角色)

In that article, we also concluded that we had to check for programmable database objects (procedure, function…) using the database user as execution context. We will ignore this case in our tests because, in SQL Server 2012 (and maybe in higher versions), a database role cannot be impersonated. To be convinced, we could give a try and run following statement that creates a stored procedure which should be executed as db_owner built-in database role.

在那篇文章中 ,我们还得出结论,我们必须使用数据库用户作为执行上下文来检查可编程数据库对象(过程,函数…)。 在测试中,我们将忽略这种情况,因为在SQL Server 2012(以及更高版本)中,无法模拟数据库角色。 确信地说,我们可以尝试运行以下语句来创建一个存储过程,该存储过程应作为db_owner内置数据库角色执行。


CREATE PROCEDURE [ApplicationSchema1].[sp_ExecuteAsDbOwner]
WITH EXECUTE AS 'db_owner'
AS
BEGINSELECT @@SERVERNAME
END;
GO

After execution, we’ll get following error message:

执行后,我们将收到以下错误消息:

In following, we’ll define a test case and scripts to build this test case like we did in the article mentioned above “How to drop a SQL Server Login and all its dependencies“. Then we’ll define a general stored procedure called DropDatabasePrincipal that will work for both Database Users and Database Roles (except we’ll focus on Database Roles for the purpose of this article).

接下来,我们将定义一个测试用例和脚本来构建该测试用例,就像我们在上文“ 如何删除SQL Server登录名及其所有依赖项 ”中提到的那样。 然后,我们将定义一个称为DropDatabasePrincipal的通用存储过程,该存储过程将对数据库用户和数据库角色均有效(但出于本文的目的,我们将重点放在数据库角色上)。

测试用例情况 (Test case situation)

Explanation

说明

In this section, we will present the test case situation to which we will refer in the remaining of this article and where we want to drop a database role called RoleToBeDropped.

在本节中,我们将介绍测试用例的情况,在本文的其余部分中将涉及该用例,并在其中要删除名为RoleToBeDropped的数据库角色。

We are in a database called [TestDb].

我们在一个名为[TestDb]的数据库中。

  • The database role RoleToBeDropped owns:

    • A Schema called [ApplicationSchema1]
    • Another database role called [RoleA]
  • 数据库角色RoleToBeDropped拥有:
    • 名为[ApplicationSchema1]的架构
    • 另一个数据库角色称为[RoleA]
    • A user called [ApplicationSQLUser1].
    • 名为[ApplicationSQLUser1]的用户。
  • A procedure called [sp_ExecuteAsRole2BD] in [ApplicationSchema1] database schema:
    • references [ApplicationSQLUser1] database user
    • can be executed by a database user called [UserB] i.e. this user has GRANT EXECUTE permission on this procedure. This permission has been granted by [RoleToBeDropped] database role.
  • 在[ApplicationSchema1]数据库模式中称为[sp_ExecuteAsRole2BD]的过程:
    • 引用[ApplicationSQLUser1]数据库用户
    • 可以由名为[UserB]的数据库用户执行,即该用户对此过程具有GRANT EXECUTE权限。 此权限已由[RoleToBeDropped]数据库角色授予。
  • The Database User [dbo] has granted following permissions to RoleToBeDropped database role:
    • CONNECT WITH GRANT OPTION
    • ALTER ON DATABASE::[TestDb]
  • 数据库用户[dbo]已授予RoleToBeDropped数据库角色以下权限:
    • 连接许可选项
    • 更改数据库:: [TestDb]

Diagram that depicts the situation

描绘情况的图

Here is a diagram that represents the situation described in previous subsection.

这是代表前面小节中描述的情况的图。

Creation script

创建脚本

You will find below, the necessary commands to create this situation in your environment. You can run the script multiple times it will work every time. This script can also be downloaded at the end of this article.

您将在下面找到在您的环境中创建这种情况所需的命令。 您可以多次运行该脚本,每次都会运行。 也可以在本文结尾处下载此脚本。


USE [TestDb];
GOIF NOT EXISTS (SELECT * FROM sys. database_principals WHERE name = N'RoleToBeDropped' AND type = 'R')CREATE ROLE [RoleToBeDropped];
GOIF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = N'ApplicationSchema1')EXEC sys.sp_executesql N'CREATE SCHEMA [ApplicationSchema1] AUTHORIZATION [RoleToBeDropped];'
ELSEEXEC sys.sp_executesql N'ALTER AUTHORIZATION  ON SCHEMA::[ApplicationSchema1] TO [RoleToBeDropped];'
GO  IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'RoleA' AND type = 'R')CREATE ROLE [RoleA] AUTHORIZATION [RoleToBeDropped];
ELSEEXEC sys.sp_executesql N'ALTER AUTHORIZATION  ON ROLE::[RoleA] TO [RoleToBeDropped];'
GOIF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'ApplicationSQLUser1')CREATE USER [ApplicationSQLUser1] WITHOUT LOGIN WITH DEFAULT_SCHEMA=[dbo];
GOIF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'UserB')CREATE USER [UserB] WITHOUT LOGIN WITH DEFAULT_SCHEMA=[dbo];
GOALTER ROLE [RoleToBeDropped] ADD MEMBER [ApplicationSQLUser1]
GOGRANT CONNECT TO [RoleToBeDropped] WITH GRANT OPTION  AS [dbo];
GOGRANT CONNECT TO [RoleToBeDropped] WITH GRANT OPTION  AS [dbo];
GOGRANT ALTER ON DATABASE::[TestDb] TO [RoleToBeDropped] WITH GRANT OPTION  AS [dbo];
GO/*Create stored procedure referencing ApplicationSQLUser1 in TestDb
*/IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[ApplicationSchema1].[sp_ExecuteAsRole2BD]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [ApplicationSchema1].[sp_ExecuteAsRole2BD] AS SELECT 1'
END
GOALTER PROCEDURE [ApplicationSchema1].[sp_ExecuteAsRole2BD]
WITH EXECUTE AS 'ApplicationSQLUser1'
AS
BEGINSELECT @@SERVERNAME
END;GOgrant execute on OBJECT::[ApplicationSchema1].[sp_ExecuteAsRole2BD] to UserB AS RoleToBeDropped;
GO

构建存储过程以删除数据库主体 (Building a stored procedure to drop database principals)

Design of the interface

界面设计

As there are common actions to perform in order to remove both database users and database roles, we will define a stored procedure that will handle both of them. We will call this procedure [DropDatabasePrincipal] and store it a database schema called [Administration].

由于要删除数据库用户和数据库角色,需要执行一些常见的操作,因此我们将定义一个存储过程来处理这两个数据库。 我们将此过程称为[DropDatabasePrincipal]并将其存储在名为[Administration]的数据库模式中。

Which parameters do we need for that procedure to do the task we want?

该过程需要哪些参数来完成所需的任务?

First, it needs a parameter to tell it which database is used. Let’s say @DatabaseName. By default, if no value is provided, the stored procedure will use the results of DB_NAME() built-in function.

首先,它需要一个参数来告诉它使用了哪个数据库。 假设@DatabaseName 。 默认情况下,如果未提供任何值,则存储过程将使用DB_NAME()内置函数的结果。

Then, we also need to provide the name of the database principal that we want to delete. We will simply call this parameter @PrincipalName.

然后,我们还需要提供要删除的数据库主体的名称。 我们将简单地将此参数称为@PrincipalName 。

As we will have to check for permission assigned by the database principal, we could also add a conditional parameter that tells the procedure whether to reassign these permissions or not. We will call this parameter @PreserveAssignedPermissions. It would be of BIT data type with a default value of 1 as we don’t want to break what will remain.

由于我们将必须检查数据库主体分配的权限,因此我们还可以添加一个条件参数,该参数告诉该过程是否重新分配这些权限。 我们将此参数称为@PreserveAssignedPermissions 。 这将是BIT数据类型,默认值为1,因为我们不想破坏剩余的数据。

If we want to keep assigned permissions, this means we need to reassign them using another database principal. You will find in “Appendix A – Assigning database permissions” some considerations that lead to the conclusion that we won’t define a @NewPermissionAssigner parameter and will simply use the default context.

如果要保留分配的权限,这意味着我们需要使用另一个数据库主体重新分配它们。 您将在“ 附录A –分配数据库权限 ”中找到一些注意事项,这些注意事项得出的结论是,我们不会定义@NewPermissionAssigner参数,而只会使用默认上下文。

In the same logic, we could want to reassign permissions this role provides to its members directly to them. Whether we do this or not would be defined by another parameter we would call @AssignRolePermissionsToItsMembers of BIT data type with a default value of 0. Again, we’ll use the default behavior of SQL Server to reassign these permissions when this parameter is set to 1.

按照相同的逻辑,我们可能希望将角色直接为其成员提供的权限重新分配给他们。 无论是否执行此操作,都将由另一个参数定义,我们将调用BIT数据类型的@AssignRolePermissionsToItsMembers ,其默认值为0。同样,当此参数设置为时 ,我们将使用SQL Server的默认行为来重新分配这些权限。 1。

Furthermore, we could tell the stored procedure whether to reassign database object ownership or not and if so, which database principal has to be used. For that purpose, we will define two additional parameters:

此外,我们可以告诉存储过程是否重新分配数据库对象所有权,如果要重新分配,则必须使用哪个数据库主体。 为此,我们将定义两个附加参数:

  1. @AlterDbObjectsOwnership, a Boolean value which set to 1 will tell the stored procedure to change the owner of database objects
  2. @AlterDbObjectsOwnership ,设置为1的布尔值将告诉存储过程更改数据库对象的所有者
  3. @NewDbObjectOwner that will be used only if former parameter is set to 1 and tell the stored procedure which database principal should be used to perform the object ownership reassignment
  4. @NewDbObjectOwner仅在将前一个参数设置为1并告诉存储过程应使用哪个数据库主体来执行对象所有权重新分配时才使用

Finally, there are general parameters:

最后,有一些常规参数:

  • @WithLog that tells the stored procedure to whether log the call and its outcome into a logging table or not keeping any trace of that call
  • @WithLog告诉存储过程是否将调用及其结果记录到日志表中,或者不保留该调用的任何痕迹
  • @Debug that, set to 1, will make the stored procedure more talkative.
  • @Debug设置为1,将使存储过程更加健谈。
  • @CheckOnly that, when set to 1, will make the stored procedure stop after all checks were passed and no action will be performed for actual principal removal. We’ll let this set to 1 by default in order to prevent from human mistakes.
  • @CheckOnly设置为1时,将使存储过程在通过所有检查后停止,并且将不执行任何操作来删除实际的主体。 为了防止人为错误,我们将其默认设置为1。

Everything put together, this leads us to following interface for our stored procedure:

一切都放在一起,这将导致我们为存储过程找到以下接口:


PROCEDURE [Administration].[DropDatabasePrincipal] (@DatabaseName                       VARCHAR(256),@PrincipalName                      VARCHAR(256),@PreserveAssignedPermissions        BIT             = 1,@AssignRolePermissionsToItsMembers  BIT             = 0,@AlterDbObjectsOwnership            BIT             = 1, @NewDbObjectOwner                   VARCHAR(256)    = 'dbo',@WithLog                            BIT             = 1,@RunCheckOnly                       BIT             = 1,@Debug                              BIT             = 0
)

Procedure workflow

程序工作流程

Here are the steps which should be in the stored procedure:

这是存储过程中应包含的步骤:

    • Check if the database exists 检查数据库是否存在
    • Check if the principal exists 检查主体是否存在
    • Check if the principal can be dropped 检查是否可以删除主体
    • Consider @PreserveAssignedPermissions and @AssignRolePermissionsToItsMember. If either of these two parameters is set to 1, then check that database principal used as new permission assigner exists and is different from the principal that will be dropped.
    • 考虑@PreserveAssignedPermissions和@AssignRolePermissionsToItsMember 。 如果这两个参数之一设置为1,则检查用作新权限分配器的数据库主体是否存在,并且与要删除的主体不同。
    • If @AlterDbObjectsOwnership = 1 then check that database principal used as new object owner exists and is different from the database principal that will be dropped.
    • 如果@AlterDbObjectsOwnership = 1,则检查用作新对象所有者的数据库主体是否存在,并且与将要删除的数据库主体不同。
    • If the database principal is a user, check that it’s not set as the execution context for one or more procedures, functions (etc.) 如果数据库主体是用户,请检查是否未将其设置为一个或多个过程,函数(等等)的执行上下文。
  1. Get the list of permissions assigned by @PrincipalName into a temporary table called #AssignedPermissions.
    • If @PreserveAssignedPermissions is set to 0 and there are permissions in that list, then fail
  2. 将@PrincipalName分配的权限列表获取到名为#AssignedPermissions的临时表中。
    • 如果@PreserveAssignedPermissions设置为0并且该列表中有权限,则失败
  3. Get the list of database objects owned by @PrincipalName
    • Schemas
    • Roles

    If @AlterDbObjectsOwnership is set to 0 and there are objects in that list, then fail

  4. 获取@PrincipalName拥有的数据库对象的列表
    • 模式
    • 的角色

    如果@AlterDbObjectsOwnership设置为0并且该列表中有对象,则失败

  5. Get the list of roles for which @PrincipalName is a member.
  6. 获取@PrincipalName是成员的角色列表。
    • get the list of its own members. 获取自己的成员列表。
    • If @AssignRolePermissionsToItsMembers is set to 1, get the list of permissions directly assigned to @PrincipalName.
    • 如果@AssignRolePermissionsToItsMembers设置为1,则获取直接分配给@PrincipalName的权限列表。
  7. If @CheckOnly parameter is set to 1, jump to step 8.
  8. 如果@CheckOnly参数设置为1,则跳到步骤8。
    • If necessary, assign database object membership to @NewDbObjectOwner.
    • 如有必要,将数据库对象成员资格分配给@NewDbObjectOwner 。
      • Remove all its members 删除其所有成员
      • If necessary, assign them permissions that were actually assigned to @PrincipalName.
      • 如有必要,向他们分配实际上已分配给@PrincipalName的权限。
    • Fire DROP ROLE command.
    • 射击DROP ROLE命令。
    • If necessary, assign back permissions previously assigned by @PrincipalName.
    • 如有必要,请分配先前由@PrincipalName分配的权限 。
  9. Perform temporary tables cleanups 执行临时表清理

Implementation details

实施细节

Getting back permissions assigned by a database principal

检索数据库主体分配的权限

In order to get back permissions assigned by a principal name, we will create a temporary table called #AssignedPermissions and insert rows returned by a modified version of following query:

为了获取由主体名称分配的权限,我们将创建一个名为#AssignedPermissions的临时表,并插入以下查询的修改版本返回的行:


select CASE WHEN state_desc = 'GRANT_WITH_GRANT_OPTION' THEN 'GRANT' ELSE state_desc END + ' ' + permission_name + ' ON ' +
CASE WHEN class_desc = 'OBJECT_OR_COLUMN' THEN'OBJECT::' + QUOTENAME(OBJECT_SCHEMA_NAME(major_id)) + '.' + QUOTENAME(OBJECT_NAME(major_id))WHEN class_desc = 'DATABASE' THEN'DATABASE::' + DB_NAME() WHEN class_desc = 'SCHEMA' THEN'SCHEMA::' + QUOTENAME(OBJECT_SCHEMA_NAME(major_id))ELSE '/*TODO: handle other cases - at the moment statement will fail*/'
END +
' TO <RoleMember>' +
CASE WHEN state_desc = 'GRANT_WITH_GRANT_OPTION' THEN ' WITH GRANT OPTION'ELSE ''
END From sys.database_permissions where grantor_principal_id = USER_ID(@PrincipalName);

Getting back the list of database objects owned by a database principal

获取数据库主体拥有的数据库对象列表

Here, we won’t cover every possible type of database object, but those presented in the test case i.e. database schemas and database roles. We will keep that information in a temporary table called #OwnedDbObjects which has following structure:

在这里,我们不会介绍所有可能的数据库对象类型,而是测试用例中介绍的对象,即数据库模式和数据库角色。 我们将这些信息保存在名为#OwnedDbObjects的临时表中,该表具有以下结构:


CREATE TABLE #OwnedDbObject (ODbObjId   INT IDENTITY(1,1), -- allow loop on recordsObjectName VARCHAR(256),     ObjectType VARCHAR(16) -- schema/role/etc.);

In order to get back database schemas, we’ll simply query sys.schemas table with a value for its principal_id column corresponding to the identifier for the database principal we want to drop.

为了获取数据库模式,我们将简单地查询sys.schemas表,并为其Principal_id列的值与我​​们要删除的数据库主体的标识符相对应。

Here is the query we’ll use:

这是我们将使用的查询:


SelectName'SCHEMA'
from sys.schemas
where principal_id = USER_ID(@PrincipalName)
;

In order to get back database roles owned by a database principal, we’ll just query sys.database_principals table with value for owning_principal_id corresponding to the identifier for the database principal we want to drop.

为了找回数据库主体所拥有的数据库角色,我们只需要查询sys.database_principals表,该表中的owning_principal_id值对应于我们要删除的数据库主体的标识符。

This can be performed using following query:

可以使用以下查询执行此操作:


selectname, 'ROLE'
from sys.database_principals
where type='R'  -- only roles
and owning_principal_id = USER_ID(@PrincipalName)
;

List database principal role memberships

列出数据库主要角色成员身份

We’ll just store this information in the same table as the above subsection, i.e. #OwnedDbObject with ‘MEMBERSHIP’ as the value for ObjectType column.

我们仅将这些信息存储在与上述小节相同的表中,即#OwnedDbObject,其中“ MEMBERSHIP”作为ObjectType列的值。

In order to list out database roles which has a database principal as a member, we’ll query sys.database_role_members system table where its column called member_principal_id has a value corresponding to the identifier for the database principal we want to drop.

为了列出具有数据库主体作为成员的数据库角色,我们将查询sys.database_role_members系统表,其中其名为member_principal_id的列的值对应于我们要删除的数据库主体的标识符。

Hence, we will use an adaptation of following query:

因此,我们将使用以下查询的改编:


selectdp.name, 'MEMBERSHIP'
from sys.database_role_members drm
inner join sys.database_principals dp
on dp.principal_id= drm.role_principal_id
where member_principal_id = USER_ID(@PrincipalName);

List members of a database role

列出数据库角色的成员

We’ll just take a modified version of the query generated by SSMS for that part. This query will look in following system tables:

我们将只使用SSMS为该部分生成的查询的修改版本。 该查询将在以下系统表中查找:

  • sys.database_principals
  • sys.database_principals
  • sys.database_role_members
  • sys.database_role_members

Stored procedure body

存储过程主体

You will find the script for creating the stored procedure in the Download section of this article.

您可以在本文的“ 下载”部分中找到用于创建存储过程的脚本。

测试我们的存储过程 (Testing our stored procedure)

You will find, attached at the end of the script, a series of tests that have been performed in order to check that a database role has been dropped as expected.

在脚本末尾,您会发现已经执行了一系列测试,以检查是否已按预期删除了数据库角色。

The first test consists of running the stored procedure with @AlterDbObjectsOwnership set to 0, meaning that it won’t set new owner for RoleA and ApplicationSchema1 database objects. This test should fail.

第一个测试包括运行存储过程, 并将@AlterDbObjectsOwnership设置为0,这意味着它不会为RoleA和ApplicationSchema1数据库对象设置新所有者。 该测试应该失败。

Here is the corresponding procedure call:

这是相应的过程调用:


EXEC  [Administration].[DropDatabasePrincipal]@DatabaseName                        = 'TestDb',@PrincipalName                       = 'RoleToBeDropped',@AssignRolePermissionsToItsMembers   = 0,@AlterDbObjectsOwnership             = 0,@NewDbObjectOwner                    = 'dbo',@RunCheckOnly                        = 0,@Debug                               = 0
;

Next test consists in setting @PreserveAssignedPermissions parameter to 0 and @AlterDbObjectsOwnership back to its default value (1). This test should fail too as permissions are assigned and we don’t want to break everything just to drop a single database principal.

下一个测试包括将@PreserveAssignedPermissions参数设置为0,并将@AlterDbObjectsOwnership返回其默认值(1)。 由于分配了权限,该测试也将失败,我们不想仅仅为了删除一个数据库主体就破坏一切。

The T-SQL script to run this test is built around following code:

运行此测试的T-SQL脚本围绕以下代码构建:


EXEC  [Administration].[DropDatabasePrincipal]@DatabaseName                        = 'TestDb',@PrincipalName                       = 'RoleToBeDropped',@PreserveAssignedPermissions         = 0,@AssignRolePermissionsToItsMembers   = 0,@AlterDbObjectsOwnership             = 1,@NewDbObjectOwner                    = 'dbo',@RunCheckOnly                        = 0,@Debug                               = 0
;

Another test will consist in setting the value of @PreserveAssignedPermissions parameter back to its default value (1) and running the stored procedure. This should actually work but, in cases where the database principal is a role, the permissions assigned to this role has not been transmitted to its members (before the execution of the stored procedure). This test is performed running following procedure call:

另一个测试将包括将@PreserveAssignedPermissions参数的值设置回其默认值(1),然后运行存储过程。 这实际上应该可以工作,但是在数据库主体是角色的情况下,分配给该角色的权限尚未传输到其成员(在执行存储过程之前)。 通过以下过程调用执行此测试:


EXEC  [Administration].[DropDatabasePrincipal]@DatabaseName                        = 'TestDb',@PrincipalName                       = 'RoleToBeDropped',@PreserveAssignedPermissions         = 1,@AssignRolePermissionsToItsMembers   = 0,@AlterDbObjectsOwnership             = 1,@NewDbObjectOwner                    = 'dbo',@RunCheckOnly                        = 0,@Debug                               = 0
;

There is a last but not least test to perform where we want to transmit permissions from role to its former members, which we achieved with @AssignRolePermissionsToItsMembers parameter set to 1. Like the test just above, it should be successful.

有一个最后但并非最不重要的测试,执行我们要从角色向其先前成员传输权限的操作,这是通过将@AssignRolePermissionsToItsMembers参数设置为1来实现的。就像上面的测试一样,它应该成功。

资料下载 (Downloads)

  • Test Case Setup Script 测试用例设置脚本
  • Test Case Cleanup Script 测试用例清理脚本
  • Test 1 测试1
  • Test 2 测试2
  • Test 3 测试3
  • Test 4 测试4
  • [Administration].[DropDatabasePrincipal] Stored procedure [管理]。[DropDatabasePrincipal]存储过程
  • All in one bundle ZIP 多合一ZIP

附录A –分配数据库权限 (Appendix A – Assigning database permissions)

In the article entitled “How to drop a SQL Server Login and all its dependencies“, we already made a few tests and we know that SQL Server resets permissions on an object when ALTER AUTHORIZATION instruction is used. Here are some additional considerations that has to be taken in order to build the part of the DropDatabasePrincipal stored procedure that sets back permissions to the other database principals that should remain after it did its work.

在标题为“ 如何删除SQL Server登录名及其所有依赖项 ”的文章中,我们已经进行了一些测试,并且我们知道使用ALTER AUTHORIZATION指令时SQL Server会重置对象的权限。 为了构建DropDatabasePrincipal存储过程的一部分,必须考虑一些其他注意事项,该部分将对其他数据库主体的权限设置回去,使其在完成工作后仍应保留。

First, let’s say [ApplicationSchema1] database schema is owned by [UserB] and we want to assign EXECUTE permission on a stored procedure in that schema to a role called [RoleA] as [dbo] database user. We would do this using following query:

首先,假设[ApplicationSchema1]数据库模式归[UserB]所有 ,我们想将该数据库中存储过程的EXECUTE权限分配给名为[RoleA]的角色,作为[dbo]数据库用户。 我们将使用以下查询执行此操作:


grant execute
on OBJECT::[ApplicationSchema1].[sp2Execute]
to RoleA
AS dbo

Unfortunately, this statement will fail with following error message:

不幸的是,该语句将失败,并显示以下错误消息:

This is due to the fact that [dbo] users, even if it’s in db_owner database role does not have sufficient permissions to run this statement. In fact, in order to do this, we should first grant [dbo] the permission to execute the stored procedure and to share this permission:

这是由于[dbo]用户(即使它是db_owner数据库角色)也没有足够的权限来运行此语句。 实际上,为了做到这一点,我们应该首先授予[dbo]执行存储过程并共享此权限的权限:


grant execute
on OBJECT::[ApplicationSchema1].[sp2Execute]
to dbo WITH GRANT OPTION ;

But, this will also fail!

但是,这也会失败!

By the way, if there were a UserC database user that has the EXECUTE permission with GRANT OPTION on that object, following statement would work:

顺便说一句,如果有一个UserC数据库用户具有对该对象的GRANT OPTION的EXECUTE权限,则以下语句将起作用:


grant execute
on OBJECT::[ApplicationSchema1].[sp2Execute]
to UserC WITH GRANT OPTION AS RoleToBeDropped;

And we could assign permission to RoleA as UserC.

然后我们可以将RoleA的权限分配为UserC 。


grant execute
on OBJECT::[ApplicationSchema1].[sp2Execute]
to RoleA
AS UserC

We can check permissions to RoleA and we’ll get:

我们可以检查RoleA的权限,并获得:

At second, let’s notice that, by default, if we grant permissions on an object in a database schema, the database principal used to grant this permission is actually the one that owns the schema.

第二,请注意,默认情况下,如果我们授予数据库模式中的对象权限,则用于授予该权限的数据库主体实际上就是拥有该模式的数据库主体。

This means that we should operate as follows in order to set back permissions after a reassignment of schema ownership:

这意味着我们应该如下操作,以便在重新分配架构所有权后撤消权限:

  1. List permissions assigned using the database principal we want to drop 列出使用我们要删除的数据库主体分配的权限
  2. Fire the ALTER AUTHORIZATION statement for each schema
  3. 对每个模式触发ALTER AUTHORIZATION语句
  4. Reassign collected permissions as new schema owner 将收集的权限重新分配为新的架构所有者

翻译自: https://www.sqlshack.com/drop-role-sql-server-database/

如何在SQL Server数据库中删除角色相关推荐

  1. 如何在SQL Server数据库中加密数据

    如何在SQL Server数据库中加密数据 为了防止某些别有用心的人从外部访问数据库,盗取数据库中的用户姓名.密码.信用卡号等其他重要信息,在我们创建数据库驱动的解决方案时,我们首先需要考虑的的第一条 ...

  2. mysql raiserror_RAISERROR在SQL Server数据库中的用法

    raiserror  是由单词 raise error 组成 raise  增加; 提高; 提升 raiserror 的作用: raiserror 是用于抛出一个错误.[ 以下资料来源于sql ser ...

  3. 如何对两个大型SQL Server数据库中的数据进行快速估计比较,以查看它们是否相等

    Bringing impactful analysis into a data always comes with challenges. In many cases, we rely on auto ...

  4. 在ASP.NET中将图片存储到Sql Server数据库中

    在ASP.NET中将图片存储到Sql Server数据库中 http://hi.baidu.com/rayshow/blog/item/360e8ff9662c8b5a252df268.html 索引 ...

  5. 清空SQL Server数据库中所有表数据的方法(转)

    清空SQL Server数据库中所有表数据的方法 其实删除数据库中数据的方法并不复杂,为什么我还要多此一举呢,一是我这里介绍的是删除数据库的所有数据,因为数据之间可能形成相互约束关系,删除操作可能陷入 ...

  6. SQL Server数据库中、获得刚插入新记录的自动ID号

    以下的文章主要是介绍在SQL Server数据库中正确获得刚插入一条新记录的自动ID号的实际操作过程,下面就是文章对其的主要内容的具体描述,望大家在浏览之后会对其有更深的了解. 使用[IDENT_CU ...

  7. 从sqlserver中数据写入mysql_[SQL Server]SQL Server数据库中如何返回INSERT INTO语句插入/写入数据后的记录值(比如ID等)?...

    问题描述 SQL Server数据库中,有时候当我们使用INSERT INTO语句写入数据后,需要返回写入数据对应的自增ID或者GUID,以便根据此记录进行后续的操作.那么SQL语句如何实现返回记录值 ...

  8. SQL Server数据库中使用sql脚本删除指定表的列

    在SQL Server数据库中删除某一列,使用数据库管理工具直接可视化操作是很简单的,但是如果要用sql脚本呢?可能你会说很简单,直接用 ALTER TABLE [要删除的字段所在的表名] DROP ...

  9. 浅析SQL Server数据库中的伪列以及伪列的含义

    浅析SQL Server数据库中的伪列以及伪列的含义 原文:浅析SQL Server数据库中的伪列以及伪列的含义 本文出处:http://www.cnblogs.com/wy123/p/6833116 ...

最新文章

  1. linux libffi 简介 高级语言互调库
  2. CentOS下软件的安装
  3. OO ALV 工具栏对于的功能码
  4. 【数据分析】干货!一文教会你 Scrapy 爬虫框架的基本使用
  5. 反转!物联网火爆,开发者却很难入门?
  6. 本科是最底层?学历真的那么重要么?
  7. 日平均血糖与糖化血红蛋白对照关系
  8. Unity的NewInputSystem的InputManager实现多玩家的总结
  9. 设计模式GOF23大纲
  10. springboot的配置文件加载的顺序,以及在不同位置配置下,加载的顺序
  11. Js脚本之jQuery学习笔记(1)
  12. html 转换 pdf 工具
  13. plsqldev 乱码
  14. js截取中英文字符串
  15. 国内各大安卓应用市场的不同ASO优化点
  16. 华硕固件 mysql_刷华硕固件后的桥接中继教程
  17. 2021 HTML面试题(最新)不定时更新
  18. 服务器虚拟化专用ovf模板,科学网—开放虚拟化格式规范2.0.0——OVF package - 唐宏伟的博文...
  19. 微信小程序登录注册demo+java服务器(一)
  20. 清北学堂 2017-10-06

热门文章

  1. python版本切换_如何在cmd下切换不同版本的Python
  2. 科蒂斯控制器故障代码_卡特挖掘机故障代码【收藏备用】
  3. LiveNVR高性能稳定RTSP、Onvif探测流媒体服务配置通道接入海康、大华等摄像机进行全终端无插件直播...
  4. 创建模态对话框和非模态对话框
  5. 更改TFS项目中的SharePoint网站端口
  6. MS CRM如果在Tab页中有Iframe选项,原来速度不慢,突然速度变慢
  7. unity3D---鼠标、键盘输入
  8. 【零基础学Java】—多线程(四十九)
  9. JavaScript学习(七十六)—this的指向问题
  10. ES6学习(九)—Generator 函数的语法