【发布时间】:2018-03-27 00:05:37
【问题描述】:
我在 SQL Server 中有一个存储过程,旨在从不同服务器创建的备份文件中恢复数据库。它动态构造语句以从磁盘上的备份文件恢复,在数据库中创建一个新用户(因为它正在恢复到不同的服务器),在两个不同的模式中授权用户,将用户分配给两个不同的角色,然后删除旧用户(来自另一台服务器上的旧数据库)。
存储过程代码如下(抱歉太长了),但为了测试它,我添加了一个标志参数(@exec bit = 0)来控制它是否实际执行了它生成的 SQL 语句,或者只是将它们打印出来.如果您通过@exec = 1,它会执行 SQL,但如果不是,它只会将它们打印出来,以便我可以单独运行它们以测试动态 SQL 代码生成。由于存储过程代码调用restore,不能恢复当前数据库,只能从同一台服务器上的另一个数据库运行,但在存储过程中,执行Restore后,将当前数据库切换到新恢复的数据库执行剩余的 SQL 语句。它构造的SQL语句如下:
Restore Database [newDBName]
from disk = N'\\BTSSqlTest1\lien_refreshes\BackUps\LASDB.1.31s100.bak'
with File = 1,
move N'lasdb' to N'E:\lien_refreshes\SQLData\newDBName.mdf',
move N'lasdb_log' to N'E:\lien_refreshes\SQLData\newDBName.ldf',
NoUnload, Replace, Stats = 25;
Use [newDBName]
Create User [domain\NewUserName]
Grant execute to [domain\NewUserName]
Alter Authorization On Schema::[db_backupoperator] to [domain\NewUserName]
Alter Authorization On Schema::[db_Owner] to [domain\NewUserName]
sp_AddRolemember 'db_backupoperator', domain\NewUserName'
sp_AddRolemember 'db_owner', domain\NewUserName'
sp_DropUser [domain\OldUserName]
当我在 SQL Server 企业管理器中单独运行这些语句时,一切都按设计运行。
当我使用@exec=1 运行存储过程时,它会使用Exec ([SQL]) 单独执行每个语句。然而,尽管除了抛出错误的 Drop User 语句之外,一切似乎都成功完成
数据库中不存在用户
运行存储过程的结果:
25 percent processed.
50 percent processed.
75 percent processed.
100 percent processed.
Processed 779144 pages for database 'newDBName', file 'lasdb' on file 1.
Processed 6 pages for database 'newDBName', file 'lasdb_log' on file 1.
RESTORE DATABASE successfully processed 779150 pages in 11.532 seconds (527.845 MB/sec).
Database [newDBName] restored.
Switched to Database [newDBName].
Execute granted to user [domain\NewUserName].
User [domain\NewUserName] authorized in schema db_backupoperator.
User [domain\NewUserName] authorized in schema db_Owner.
User [domain\NewUserName] added to role db_backupoperator.
User [domain\NewUserName] added to role db_Owner.
Msg 15008, Level 16, State 1, Procedure sp_dropuser, Line 12
User domain\OldUserName' does not exist in the current database.
Dropped User [domain\OldUserName].
当我查看生成的数据库本身时,(即使在刷新后)新创建的用户不在数据库中,而来自另一台服务器的旧用户仍然存在。
******************************************************
****** Stored proc ***********************************
Create PROCEDURE RestoreProdTest
@fileSpec nvarchar(400),
@exec bit = 0
As
Set NoCount On
declare @nl Char(2) = char(13) + char(10)
declare @2nl char(4) = @nl + @nl
-- --------------------------------
declare @debugMsg varchar(max) = 'Variable Values:' + @nl
declare @sqlCode varchar(max) = 'Executable SQL Code:' + @nl
declare @dbNm nvarchar(50) = 'newDBName'
declare @user varchar(40) = 'domain\NewUserName'
declare @dbId int = DB_Id(@dbNm)
-- ----------------------------------------------------------------
Declare @tab Table
(logNm varchar(256), phyNm varchar(300),
Typ varchar, FilGrpNm varChar(128), Siz varchar(128),
MaxSize varChar(128), FileId varchar(128),
CreateLSN varChar(128),
DropLSN varchar(128), UniqueId varChar(128),
ROLSN varchar(128),
RWLSN varchar(128), BkSizBytes varChar(128),
SrceBlckSize varchar(128),
FileGrpId varchar(128), LogGrpId varChar(128),
DiffBaseLSN varchar(128),
DiffBaseGUID varchar(128), IsReadOnly varChar(128),
IsPresent varchar(128), ThumbPrint varchar(128))
-- ----------------------------------------------------------
Insert @tab(logNm, phyNm, Typ, FilGrpNm, Siz, MaxSize, FileId,
CreateLSN, DropLSN, UniqueId, ROLSN, RWLSN, BkSizBytes,
SrceBlckSize, FileGrpId, LogGrpId, DiffBaseLSN,
DiffBaseGUID, IsReadOnly, IsPresent, ThumbPrint)
Exec('Restore fileListOnly from disk=''' + @fileSpec + '''')
declare @oldDataFileSpec varChar(400),
@oldLogFileSpec varChar(400)
Set @oldDataFileSpec = (Select logNm from @tab where Typ = 'D')
Set @oldLogFileSpec = (Select logNm from @tab where Typ = 'L')
-- -------------------------------------
declare @dataFile varChar(400)
declare @logFile varChar(400)
Select @dataFile = physical_name
from sys.Master_Files
Where Database_Id = @dbId and type = 0
Select @logFile = physical_name
from sys.Master_Files
Where Database_Id = @dbId and type = 1
declare @killSql nVarChar(200) = 'msdb.dbo.sp_KillUserProc '
declare @restoreSql nVarChar(1000) =
N'Restore Database [' + @dbNm + ']' + @nl +
'from disk = N''' + @fileSpec + ''' with File = 1,' + @nl +
' move N''' + @oldDataFileSpec + '''' + ' to N''' +
@dataFile + ''',' + @nl +
' move N''' + @oldLogFileSpec + '''' + ' to N''' +
@logFile + ''',' + @nl +
' NoUnload, Replace, Stats = 25;'
declare @spids table (spid integer primary key not null)
insert @spids(spid)
select session_id from sys.dm_exec_sessions
where database_id = @dbId
-- ----------------------
declare @spid int = 0
declare @spidstr varchar(4)
while exists (select * from @spids where spid > @spid) begin
Select @spid = min(spid) from @spids where spid > @spid
set @spidstr = format(@spid, '0')
set @sqlCode += @killSql + @spidstr + @nl
end
-- --------------------------------------------
if @exec = 1 Begin
Set @spid = 0
while exists (select * from @spids where spid > @spid) begin
Select @spid = min(spid) from @spids where spid > @spid
set @spidstr = format(@spid, '0')
exec(@killSql + @spidstr)
end
-- ------------------------------------------------
exec (@restoreSql)
print ' Database [' + @dbNm + '] restored.'
end
else Set @sqlCode += @restoreSql + @2nl
-- Switch to new restored database
declare @UseSql nVarChar(100) = 'Use [' + @dbNm + ']'
if @exec = 1 begin
exec (@UseSql)
print 'Switched to Database [' + @dbNm + '].'
end else Set @sqlCode += @UseSql + @2nl
-- Grant execute permissions (also creates the user)
declare @grantSql nVarChar(1000) = N'Grant execute to [{User}]'
Set @grantSql = Replace(@grantSql, '{User}', @user)
if @exec = 1 begin
exec (@grantSql)
print 'Execute granted to user [' + @user + '].'
end else Set @sqlCode += @grantSql + @2nl
-- Assign user to schemas -------------
declare @schmSql nVarChar(200) =
N'Alter Authorization On Schema::[db_backupoperator] to [{user}]'
Set @schmSql = Replace(@schmSql, '{User}', @user)
if @exec = 1 begin
exec (@schmSql)
print 'User [' + @user + '] authrzd in schema db_backupoperator.'
end else Set @sqlCode += @schmSql + @2nl
-- ----------------------------
Set @schmSql = Replace(@schmSql, 'db_backupoperator', 'db_Owner')
if @exec = 1 begin
exec (@schmSql)
print 'User [' + @user + '] authorized in schema db_Owner.'
end else Set @sqlCode += @schmSql + @2nl
-- --------------------------------------------------
-- Grant backup operator & dbOwner Roles
declare @roleSql nVarChar(1000) =
'sp_AddRolemember ''db_backupoperator'', ''{User}'''
Set @roleSql = Replace(@roleSql, '{User}', @user)
if @exec = 1 begin
exec (@roleSql)
print 'User [' + @user + '] added to role db_backupoperator.'
end else Set @sqlCode += @roleSql + @2nl
-- ---------------------------
set @roleSql = 'sp_AddRolemember ''db_owner'', ''{User}'''
Set @roleSql = Replace(@roleSql, '{User}', @user)
if @exec = 1 begin
exec (@roleSql)
print 'User [' + @user + '] added to role db_Owner.'
end else Set @sqlCode += @roleSql + @2nl
-- ----- Drop PROD User -----------
declare @dropUserSql nVarchar(50) =
'sp_DropUser [rose\LasPROD_Svc]'
if @exec = 1 begin
exec (@dropUserSql)
print 'Dropped User [rose\LasPROD_Svc].'
end else Set @sqlCode += @dropUserSql + @2nl
-- ---------------------------
if @exec = 0 print @sqlCode
Return 0
【问题讨论】:
-
EXEC (@sql)在它自己的范围内运行该 SQL,它不会更改以下命令的当前数据库。 -
@DavidG,那么如何在新恢复的数据库中运行剩余的命令?这些命令中的大多数仅“在当前数据库中”运行,并且似乎没有要在另一个数据库中执行的语法变体(例如使用完全限定名称(
Database.Schema.Object)的选择可以。是否有另一种方法来执行这些以便它们针对新恢复的数据库执行? -
好吧,您可以通过运行
PRINT DB_NAME()来测试它,这将向您显示它正在运行的数据库上下文。我相信您可以执行上述操作的唯一方法是运行@987654331 中的所有内容@ 到最后作为单个语句。 -
@DavidG,我能想到一个选择,就是动态构造一个完整的Stored proc,里面包含所有的语句,在新恢复的数据库中创建这个新的SOPP,然后使用它的完全限定的名称。这可能行得通,但我需要在完成后将其删除。
-
一个语句可能会起作用,我需要在所有语句之间加上“Go”......我会尝试的。如果代表对您很重要,请将其添加为答案,我会将其标记为正确答案。
标签: sql sql-server database database-restore