[英]script to move multiple database log files to another disk
我有许多数据库需要将其日志文件移动到新驱动器。 有脚本可以在任何地方执行此操作吗? 有点像
对于每个用户数据库... alter database ...脱机...将日志文件移动到T:...
几年前我不得不这样做。
您需要更改@DestinationFolder值并调整查询以过滤dbs。
目前,这会将所有用户dbs的日志移动到@DestinationFolder位置。 我建议注释掉所有的exec sp_executesql @CMD命令并运行,以验证所需的命令是否正确:
USE [master] GO CREATE proc [dbo].[MoveLog] as --enabling xp_cmdshell will be required to do the file move exec sp_configure 'xp_cmdshell', 1; reconfigure; declare @DBName as varchar(128); declare @LogicalName as varchar(128); declare @FileLocation as varchar(128); declare @FileName as varchar(128); declare @CMD as nvarchar(256); declare @SourceFolder as varchar(128); declare @DestinationFolder as varchar(128); SET @DestinationFolder = 'E:\MSSQL\LOG'; -- Change this to the correct destination folder BEGIN TRY declare Db_cursor Cursor forward_only for select name from sys.databases where owner_sid 0x01;--user dbs only OPEN Db_cursor; FETCH NEXT FROM Db_cursor INTO @DBName; WHILE @@FETCH_STATUS = 0 BEGIN set @CMD = 'ALTER DATABASE [' + @DBName + '] SET SINGLE_USER WITH ROLLBACK immediate'; Exec sp_executesql @CMD RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT ; set @CMD = 'ALTER DATABASE [' + @DBName + '] SET OFFLINE '; Exec sp_executesql @CMD RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT; SELECT @LogicalName =name, @FileLocation=physical_name, @FileName = reverse(left(reverse(physical_name), charindex('\', reverse(physical_name)) -1)) FROM sys.master_files WHERE type_desc = 'LOG' and database_id = DB_ID(@DBName); --move the log file set @CMD = 'exec xp_cmdshell N''move "' + @FileLocation + '" "' + @DestinationFolder + '\' + @FileName + '"'''; Exec sp_executesql @CMD RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT; SET @SourceFolder = REPLACE(@FileLocation,@Filename,''); /* Update the system catalog */ set @CMD = 'ALTER DATABASE [' + @DBName + '] MODIFY FILE ( NAME = [' + @LogicalName + '], FILENAME = ''' + @DestinationFolder + '\' + @FileName + ''')'; Exec sp_executesql @CMD RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT; set @CMD = 'ALTER DATABASE [' + @DBName + '] SET MULTI_USER'; Exec sp_executesql @CMD RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT; set @CMD = 'ALTER DATABASE [' + @DBName + '] SET ONLINE'; Exec sp_executesql @CMD RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT; FETCH NEXT FROM Db_cursor INTO @DBName; END CLOSE Db_cursor; DEALLOCATE Db_cursor; --GO END TRY BEGIN CATCH IF (SELECT CURSOR_STATUS('global','Db_cursor')) >=0 BEGIN DEALLOCATE Db_cursor; RAISERROR ('Db_cursor Deallocated in catch',0,1) WITH NOWAIT; END SELECT ERROR_LINE(),ERROR_MESSAGE(); END CATCH exec sp_configure 'xp_cmdshell', 0; reconfigure;
我知道这是一个很老的帖子。 但我认为最好在此发布此解决方案,而不是在新帖子中发布。
我需要上面的解决方案,但由于减少了停机时间(以上脚本只抓取一个文件),因此同时更改了更多ROWS和LOG文件。 如果Destinationlocation与SourceLocation相同,我添加了测试代码以不使用数据库Ofline。 您可以为ROWS和LOG文件定义不同的目标/分区。
我错过的一步是处理文件ACL。 在某些情况下,需要一个超过默认的ACL,特别是对于服务或实例帐户。 我添加了这一步。 ACL设置由位于@copyaclScriptLocation的简单Powershell脚本处理。脚本来自Jeffrey Snover
<#
.SYNOPSIS
Copy the ACL from one file to other files
.DESCRIPTION
Takes a file and copies its ACL to one or more other files.
.PARAMETER FromPath
Path of the File to get the ACL from.
.PARAMETER Destination
Path to one or more files to copy the ACL to.
.PARAMETER Passthru
Returns an object representing the security descriptor. By default, this cmdlet does not generate any output.
.INPUTS
You can Pipeline any object with a Property named "PSPath", "FullName" or "Destination".
.EXAMPLE
PS> Copy-Acl Referencefile.txt (dir c:\temp\*xml)
.EXAMPLE
PS> dir c:\files *.xml -recurse | Copy-Acl ReferenceFile.txt
.LINK
Get-Acl
Set-Acl
.NOTES
Author: Jeffrey Snover
#>
#requires -Version 2.0
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(position=0,Mandatory=$true)]
[String]$FromPath,
[Parameter(Position=1,Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
[Alias("PSpath","fullname")]
[String[]]$Destination,
[Parameter(Mandatory=$false)]
[Switch]$PassThru
)
Begin
{
if (! (Test-Path $FromPath))
{
$ErrorRecord = New-Object System.Management.Automation.ErrorRecord (
(New-Object Exception "FromPath ($fromPath) does not point to an existing object"),
"Copy-Acl.TestPath",
"ObjectNotFound",
$FromPath
)
$PSCmdlet.ThrowTerminatingError($ErrorRecord)
}
$acl = Get-Acl $FromPath
}
Process
{
foreach ($Dest in @($Destination))
{
if ($pscmdlet.ShouldProcess($Dest))
{
Set-Acl -Path $Dest -AclObject $acl -Passthru:$PassThru
}
}
}
现在存储过程:
USE [master]
GO
IF EXISTS ( SELECT *
FROM sys.objects
WHERE type = 'P'
AND name = 'MoveData' )
DROP PROCEDURE MoveData
GO
CREATE PROC [dbo].[MoveData]
AS /* enabling show advanced options changing xpcmdshell */
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
/* enabling xp_cmdshell will be required to do the file execute bash commands */
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
DECLARE @DBName AS VARCHAR(128);
DECLARE @LogicalName AS VARCHAR(128);
DECLARE @FileLocation AS VARCHAR(128);
DECLARE @FileName AS VARCHAR(128);
DECLARE @FileType AS VARCHAR(128);
DECLARE @OwnerSid AS VARCHAR(128);
DECLARE @CMD AS NVARCHAR(256);
DECLARE @SourceFolder AS VARCHAR(128);
DECLARE @DestinationFolderDATA AS VARCHAR(128);
DECLARE @DestinationFolderLOG AS VARCHAR(128);
DECLARE @DestinationFolder AS VARCHAR(128);
DECLARE @copyaclScriptLocation AS VARCHAR(128);
DECLARE @SQLDataRootDir AS VARCHAR(128);
DECLARE @InstanceName AS VARCHAR(128);
DECLARE @Regkey AS VARCHAR(128);
/*DEFINE Your Default VARIABLES*/
SET @DestinationFolderDATA = 'V:\MSSQL\DATA'; --change this to your destination Dir for DATA Files
SET @DestinationFolderLOG = 'P:\MSSQL\DATA'; --change this to your destination Dir for LOG
SET @copyaclScriptLocation = 'V:\SCRIPTE\1_Move_Data_and_Log\copy-acl.ps1'; --change this to your destination Dir
SET @InstanceName = 'CLUSTER_EV'
/* set and create DATA Destination folder if not already exist */
SET @CMD = 'exec xp_cmdshell N''IF not exist "' + @DestinationFolderDATA
+ '"\NUL ( mkdir "' + @DestinationFolderDATA + '") && "'
+ @DestinationFolderDATA + '" does not exist... created''';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
IF ( @DestinationFolderDATA NOT LIKE @DestinationFolderLOG )
BEGIN
SET @CMD = 'exec xp_cmdshell N''IF not exist "'
+ @DestinationFolderLOG + '"\NUL ( mkdir "'
+ @DestinationFolderLOG + '") && "' + @DestinationFolderLOG
+ '" does not exist... created''';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
END
/* get dataroot location for copy original ACL */
SET @Regkey = 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10_50.'
+ @InstanceName + '\Setup';
EXEC master.dbo.xp_regread @rootkey = 'HKEY_LOCAL_MACHINE', @key = @Regkey,
@value_name = 'SQLDataRoot', @value = @SQLDataRootDir OUTPUT,
@output = 'no_output'
SELECT @SQLDataRootDir AS DataAndLogFilePath
/* Replace ACL with DEFAULT SQL ACL from Master.mdf Data Folder */
SET @CMD = 'exec xp_cmdshell N''powershell.exe ' + @copyaclScriptLocation
+ ' "' + @SQLDataRootDir + '\DATA" "' + @DestinationFolderDATA + '"''';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
IF ( @DestinationFolderDATA NOT LIKE @DestinationFolderLOG )
BEGIN
SET @CMD = 'exec xp_cmdshell N''powershell.exe '
+ @copyaclScriptLocation + ' "' + @SQLDataRootDir + '\DATA" "'
+ @DestinationFolderLOG + '"''';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
END
/* end of adding ACL Sync*/
BEGIN TRY
DECLARE Db_cursor CURSOR forward_only
FOR
SELECT DB_NAME(mf.[database_id]) [DBName] ,
mf.[type_desc] [FileType] ,
mf.[name] [LogicalName] ,
RIGHT(mf.[physical_name],
CHARINDEX('\', REVERSE(mf.[physical_name])) - 1) [FileName] ,
mf.[physical_name] [FileLocation] ,
sysdb.owner_sid [OwnerSID]
FROM sys.master_files mf
INNER JOIN sys.databases sysdb ON mf.database_id = sysdb.database_id
-- use no databases with Service Account as Owner like System Databases
WHERE sysdb.owner_sid NOT LIKE 0x01
ORDER BY DB_NAME(mf.[database_id]) ,
mf.[file_id]
OPEN Db_cursor;
FETCH NEXT FROM Db_cursor INTO @DBName, @FileType, @LogicalName,
@FileName, @FileLocation, @OwnerSID;
WHILE @@FETCH_STATUS = 0
BEGIN
IF ( ( ( @FileType LIKE 'ROWS' )
AND ( @FileLocation NOT LIKE ( @DestinationFolderDATA
+ '\' + @FileName ) )
)
OR ( ( @FileType LIKE 'LOG' )
AND ( @FileLocation NOT LIKE ( @DestinationFolderLOG
+ '\' + @FileName ) )
)
)
BEGIN
-- SET Destination for different Datatypes
IF @FileType LIKE 'ROWS'
SET @DestinationFolder = @DestinationFolderDATA
ELSE
SET @DestinationFolder = @DestinationFolderLOG
SET @CMD = 'ALTER DATABASE [' + @DBName
+ '] SET SINGLE_USER WITH ROLLBACK immediate';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
SET @CMD = 'ALTER DATABASE [' + @DBName
+ '] SET OFFLINE ';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
/* move the DATA file */
SET @CMD = 'exec xp_cmdshell N''copy "'
+ @FileLocation + '" "' + @DestinationFolder + '\'
+ @FileName + '"''';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
-- added by Steffen Engel - copy ACL Information
SET @CMD = 'exec xp_cmdshell N''powershell.exe '
+ @copyaclScriptLocation + ' "' + @FileLocation
+ '" "' + @DestinationFolder + '\' + @FileName
+ '"''';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
SET @SourceFolder = REPLACE(@FileLocation, @Filename,
'');
/* Update the system catalog */
SET @CMD = 'ALTER DATABASE [' + @DBName
+ '] MODIFY FILE ( NAME = [' + @LogicalName
+ '], FILENAME = ''' + @DestinationFolder + '\'
+ @FileName + ''')';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
SET @CMD = 'ALTER DATABASE [' + @DBName
+ '] SET MULTI_USER';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
SET @CMD = 'ALTER DATABASE [' + @DBName
+ '] SET ONLINE';
EXEC sp_executesql @CMD
RAISERROR ('Executed command: %s',0,1,@CMD) WITH NOWAIT;
END
FETCH NEXT FROM Db_cursor
INTO @DBName, @FileType, @LogicalName, @FileName,
@FileLocation, @OwnerSID;
END
CLOSE Db_cursor;
DEALLOCATE Db_cursor;
--GO
END TRY
BEGIN CATCH
IF ( SELECT CURSOR_STATUS('global', 'Db_cursor')
) >= 0
BEGIN
DEALLOCATE Db_cursor;
RAISERROR ('Db_cursor Deallocated in catch',0,1) WITH NOWAIT;
END
SELECT ERROR_LINE() ,
ERROR_MESSAGE();
END CATCH
EXEC sp_configure 'xp_cmdshell', 0;
RECONFIGURE;
EXEC sp_configure 'show advanced options', 0;
RECONFIGURE;
注释:/ *获取复制原始ACL的dataroot位置* /
我知道XP_REGREAD部件不是一个很好的解决方案,最好用Powershell完成,但我不知道比获得dataroot目录更好的方法。 此代码段用于SQL实例,很容易更改为默认路径
欢迎评论:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.