我有许多数据库需要将其日志文件移动到新驱动器。有脚本可以在任何地方执行此操作吗?
的内容为每个用户数据库... 改变数据库...... 离线...... 将日志文件移动到T:...
答案 0 :(得分:2)
您需要更改@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;
答案 1 :(得分:0)
我知道这是一篇非常古老的帖子。但我认为在这里发布此解决方案比在新帖子中发布更好。
我需要上面的解决方案,但由于减少了停机时间(以上脚本只抓取一个文件),因此同时更改了更多ROWS和LOG文件。如果Destinationlocation与SourceLocation相同,我添加了测试代码,不要使用数据库Ofline。您可以为ROWS和LOG文件定义不同的目标/分区。
我错过的一步是处理文件ACL。在某些情况下,需要一个超过默认的ACL,特别是对于服务或实例帐户。我添加了这一步。使用位于@copyaclScriptLocation的简单Powershell脚本处理ACL设置 剧本来自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的数据库位置* /
我知道XP_REGREAD Part不是一个很好的解决方案,而且使用Powershell做得更好,但我不知道比获取dataroot目录更好的方法。
此代码段用于SQL实例,可以轻松更改为默认路径
欢迎评论:)