脚本将多个数据库日志文件移动到另一个磁盘

时间:2011-09-13 15:18:55

标签: sql sql-server sql-server-2005 tsql

我有许多数据库需要将其日志文件移动到新驱动器。有脚本可以在任何地方执行此操作吗?

的内容

为每个用户数据库...   改变数据库......   离线......   将日志文件移动到T:...

2 个答案:

答案 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实例,可以轻松更改为默认路径

欢迎评论:)