Move All Your SQL Database and Log Files in Bulk

As always, the script from this post can also be found on our GitHub repo, HERE

You’ve got a SQL Server with a few hundred databases on it (to be honest it doesn’t even need to be quite that many) and you need to move all the data and log files to a new location. Perhaps you’re going to be migrating onto a new, shiny SAN or maybe your disks are just about full and you need to shift a bunch of the files off somewhere else.

The first thing that you’re going to need to do is change the paths of the files in SQL. That’s easy enough to do with an ALTER DATABASE statement.

ALTER DATABASE SQLUndercover MODIFY FILE (NAME SQLUndercover_Log, FILENAME = 'F:\SQLLogs\SQLUndercover_Log.ldf'

But that’s going to get very tedious very quickly if you’ve got to do that for a whole lotta databases. So to help out, I thought I’d share a little script that I’ve been using for a while (or a variation on it at least) to make the process far easier and generate all the ALTER statements for you.

Author: David Fowler
Revision date: 22/04/2020
Version: 1.1
This script is for personal, educational, and internal 
corporate purposes, provided that this header is preserved. Redistribution or sale 
of this script,in whole or in part, is prohibited without the author's express 
written consent. 
The software is provided "as is", without warranty of any kind, express or
implied, including but not limited to the warranties of merchantability,
fitness for a particular purpose and noninfringement. in no event shall the
authors or copyright holders be liable for any claim, damages or other
liability, whether in an action of contract, tort or otherwise, arising from,
out of or in connection with the software or the use or other dealings in the
--config variables
DECLARE @logpath NVARCHAR(260) = 'G:\SQLLogs'
DECLARE @datapath NVARCHAR(260) = 'E:\SQLData'
DECLARE @movelogs BIT = 1
DECLARE @movedata BIT = 0
--runtime variables
--uncomment the predicates to include and exclude databases as required
SELECT DB_NAME(database_id)
    ,REVERSE(SUBSTRING(REVERSE(physical_name), 0, CHARINDEX('\', REVERSE(physical_name))))
FROM sys.master_files
WHERE type IN (0,1)
--AND DB_NAME(database_id) IN ('sqlundercover')                         --uncomment to include databases
--AND DB_NAME(database_id) NOT IN ('master','tempdb','msdb','model')    --uncomment to exclude databases
DECLARE @logicalname SYSNAME
DECLARE @physicalname NVARCHAR(260)
--check filepaths finish with a \ and add if they don't
IF (SUBSTRING(@datapath, LEN(@datapath), 1) != '\')
    SET @datapath += N'\'
IF (SUBSTRING(@logpath, LEN(@logpath), 1) != '\')
    SET @logpath += N'\'
OPEN Files
FROM Files
            WHEN @type = 0 AND @movedata = 1
                THEN @datapath + @physicalname + ''')'
            WHEN @type = 1 AND @movelogs = 1
                THEN @logpath + @physicalname + ''')'
    FROM Files
    INTO @DBName

There are a couple of variables that you’re going to want to change, @logpath and @datapath should match your new log and data paths respectively and @movelogs and @movedata when set to 0 will cause the script to ignore log or data files.

You can also uncomment the lines of the WHERE clause to explicitly include or exclude certain databases.

Run that and it’ll generate all the ALTER DATABASE commands to repoint SQL at the new paths.

But What About Moving the Actual Files?

So far you’ll only have repointed SQL to the new file paths, the actual files themselves will still be where they’ve always been.

Before you can move anything, you’ll need to stop your SQL Service.

Once that’s done you can get on with moving the files. You could just do a simple copy and past from the old place to the new but that brings with it the problem that you may find that the permissions aren’t copied across quite as you’d have expected and SQL Server might have trouble accessing the file.

My preferred option is to use Robocopy from within PowerShell, you can do the same in the command prompt if you wanted. Either way, by using the /COPYALL switch, you will cause all the file attributes including the ACL (permissions) to also go over.

RoboCopy SourceLocation DestinationLocation *.* /COPYALL

You’ve probably already guessed that *.* is the file mask with * being a wildcard and meaning that you’ll be copying all files from the SourceLocation. If you happened to want to limit the copy to log files only, you could change that mask to *.ldf.

With the files all copied, it’s now just a case of bringing your SQL Server back online.

When I do these sorts of moves, I do have a PowerShell script that brings these steps as well as a few others together which I may share one day but for now I hope that’s given you an idea of how to easily migrate your SQL Server’s files in bulk, easily.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at

Up ↑

%d bloggers like this: