有时我必须将数据库从我们的生产 SQL 服务器恢复到测试 SQL 实例。当数据库恢复后,我们手动为恢复的数据库恢复正确的访问权限(例如数据库所有者/读取器/写入器)。此过程运行良好,除了必须在还原之前手动截取权限,然后从拍摄的图像中重新应用它们。
是否有一种简单的方法可以在数据库还原之前使用 T-SQL 存储用户的当前权限,然后在还原完成后重新应用这些相同的权限?
有时我必须将数据库从我们的生产 SQL 服务器恢复到测试 SQL 实例。当数据库恢复后,我们手动为恢复的数据库恢复正确的访问权限(例如数据库所有者/读取器/写入器)。此过程运行良好,除了必须在还原之前手动截取权限,然后从拍摄的图像中重新应用它们。
是否有一种简单的方法可以在数据库还原之前使用 T-SQL 存储用户的当前权限,然后在还原完成后重新应用这些相同的权限?
您的问题的答案很可能是杰夫的答案。
但是 Howard 的脚本非常实用,我只是添加了一个列,它会生成带有信息的 TSQL 语法。您可以复制它并作为 SQL 运行以将权限“复制”到另一个数据库。
SELECT
dp.permission_name collate latin1_general_cs_as AS Permission,
t.TABLE_SCHEMA + '.' + o.name AS Object,
dpr.name AS Username
, 'GRANT ' + dp.permission_name collate latin1_general_cs_as
+ ' ON '
+ t.TABLE_SCHEMA
+ '.'
+ o.name
+ ' TO '
+ dpr.name
FROM sys.database_permissions AS dp
INNER JOIN sys.objects AS o ON dp.major_id=o.object_id
INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id
INNER JOIN sys.database_principals AS dpr ON dp.grantee_principal_id=dpr.principal_id
INNER JOIN INFORMATION_SCHEMA.TABLES t
ON TABLE_NAME = o.name
WHERE dpr.name NOT IN ('public','guest')
ORDER BY
Permission, Object,Username
SELECT
dp.permission_name collate latin1_general_cs_as AS Permission,
t.TABLE_SCHEMA + '.' + o.name AS Object,
dpr.name AS Username
FROM sys.database_permissions AS dp
INNER JOIN sys.objects AS o ON dp.major_id=o.object_id
INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id
INNER JOIN sys.database_principals AS dpr ON dp.grantee_principal_id=dpr.principal_id
INNER JOIN INFORMATION_SCHEMA.TABLES t
ON TABLE_NAME = o.name
WHERE dpr.name NOT IN ('public','guest')
ORDER BY
Permission, Object,Username
有一个非常有用的功能: sys.fn_my_permissions ( securable , 'securable_class' ) 它使您可以查看当前用户对指定对象的有效权限,所以我不知道您是否可以简单地从中构建 GRANT/DENY 命令。我从来没有那样用过。在您的情况下,您将以另一个用户身份运行它:
EXECUTE AS USER = '<username>';
GO
SELECT *
FROM fn_my_permissions(null, 'SERVER')
GO
SELECT *
FROM fn_my_permissions('<DBNAME>', 'Database')
ORDER BY subentity_name, permission_name ;
REVERT;
GO
这是@Fabian 编写的脚本的修改版本,以便我还可以编写存储过程的权限。还添加了 QUOTENAME,因此适当的内容在括号中。
SELECT
dp.permission_name collate latin1_general_cs_as AS Permission,
t.TABLE_SCHEMA + '.' + o.name AS TableName,
rt.ROUTINE_SCHEMA + '.' + o.name AS ProcedureName,
dpr.name AS Username
, 'GRANT ' + dp.permission_name collate latin1_general_cs_as
+ ' ON '
+ QUOTENAME(CASE WHEN t.TABLE_SCHEMA IS NOT NULL THEN t.TABLE_SCHEMA ELSE rt.ROUTINE_SCHEMA END )
+ '.'
+ QUOTENAME(o.name)
+ ' TO '
+ QUOTENAME(dpr.name)
FROM sys.database_permissions AS dp
INNER JOIN sys.objects AS o ON dp.major_id=o.object_id
INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id
INNER JOIN sys.database_principals AS dpr ON dp.grantee_principal_id=dpr.principal_id
LEFT outer JOIN INFORMATION_SCHEMA.TABLES t ON TABLE_NAME = o.name
LEFT OUTER JOIN INFORMATION_SCHEMA.ROUTINES rt ON rt.ROUTINE_NAME = o.name
WHERE dpr.name NOT IN ('public','guest')
ORDER BY Permission, TableName, ProcedureName, Username
您正在处理的问题称为孤立用户。
这是我过去使用过的一个脚本(我不得不从内存中将它放在一起,您应该仔细验证它):
create table #users (UserName sysname, UserSID varbinary(85))
Insert into #users
exec sp_change_users_login @Action='Report';
declare mycursor cursor for select * from #users;
open mycursor;
declare @UserName sysname;
declare @UserSID varbinary(85);
fetch next from mycursor
into @UserName, @UserSID
while @@FETCH_STATUS = 0
begin
exec sp_change_users_login @Action='update_one', @UserNamePattern=@UserName, @LoginName=@UserName;
end
close mycursor;
deallocate mycursor;
这个脚本做了一个重要的假设。用户登录名和他们在数据库中的用户名匹配。如果不是这样,您将不得不更改发送到 sp_change_users_login 的@LoginName 参数。
将此与有关权限的答案结合起来:
SELECT 'EXEC sp_addrolemember @rolename ='
+ SPACE(1) + QUOTENAME(USER_NAME(rm.role_principal_id), '')
+ ', @membername =' + SPACE(1) + QUOTENAME(USER_NAME(rm.member_principal_id), '')
AS 'Role Memberships'
FROM sys.database_role_members AS rm
ORDER BY rm.role_principal_id