Di chuyển nhiều cơ sở dữ liệu từ C: sang D: cùng một lúc


17

Tôi có SQL Server 2008 R2 với 323 cơ sở dữ liệu tiêu thụ khoảng 14 GB trên ổ C: ổ SSD nhanh.

Vì tôi muốn lấy lại một số dung lượng trên ổ C: của mình, tôi muốn chuyển chúng sang ổ D: của tôi.

Tôi đã tìm thấy bài viết MSDN này , nhưng đó dường như là thủ tục để chỉ di chuyển một cơ sở dữ liệu.

Có cách nào tự động hoặc tập lệnh để di chuyển tất cả cơ sở dữ liệu của tôi cùng một lúc không?

Câu trả lời:


20

Tôi sử dụng Powershell cho loại công việc này. Trên thực tế, tôi sử dụng Powershell để tạo Powershell, bởi vì tôi có một tập lệnh sẽ lặp qua cơ sở dữ liệu của tôi và tạo tập lệnh di chuyển cuối cùng của tôi. Bạn sẽ phải di chuyển từng cơ sở dữ liệu một lần, nhưng điều này ít nhất sẽ giúp bạn kịch bản 90% công việc.

#load SMO
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
#Added line if using SQL Server 2012 or later
Import-module SQLPS
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null

#Create server object and output filename 
$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server "localhost"
$outputfile=([Environment]::GetFolderPath("MyDocuments"))+"\FileMover.ps1"

#set this for your new location
$newloc="X:\NewDBLocation"

#get your databases
$db_list=$server.Databases

#build initial script components
"Add-PSSnapin SqlServerCmdletSnapin100" > $outputfile
"Add-PSSnapin SqlServerProviderSnapin100" >> $outputfile
"Import-Module SQLPS" >> $outputfile 
"[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') `"localhost`" | out-null" >> $outputfile
"`$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server " >> $outputfile

foreach($db_build in $db_list)
{
    #only process user databases
    if(!($db_build.IsSystemObject))
    {
        #script out all the file moves
        "#----------------------------------------------------------------------" >> $outputfile
        "`$db=`$server.Databases[`""+$db_build.Name+"`"]" >> $outputfile

        $dbchange = @()
        $robocpy =@()
        foreach ($fg in $db_build.Filegroups)
        {
            foreach($file in $fg.Files)
            {
                $shortfile=$file.Filename.Substring($file.Filename.LastIndexOf('\')+1)
                $oldloc=$file.Filename.Substring(0,$file.Filename.LastIndexOf('\'))
                $dbchange+="`$db.FileGroups[`""+$fg.Name+"`"].Files[`""+$file.Name+"`"].Filename=`"$newloc`\"+$shortfile+"`""
                $robocpy+="ROBOCOPY `"$oldloc`" `"$newloc`" $shortfile /copyall /mov"

            }
        }

        foreach($logfile in $db_build.LogFiles)
        {
            $shortfile=$logfile.Filename.Substring($logfile.Filename.LastIndexOf('\')+1)
            $oldloc=$logfile.Filename.Substring(0,$logfile.Filename.LastIndexOf('\'))
            $dbchange+="`$db.LogFiles[`""+$logfile.Name+"`"].Filename=`"$newloc`\"+$shortfile+"`""
            $robocpy+="ROBOCOPY `"$oldloc`" `"$newloc`" $shortfile /copyall /mov"
        }

        $dbchange+="`$db.Alter()" 
        $dbchange+="Invoke-Sqlcmd -Query `"ALTER DATABASE ["+$db_build.Name+"] SET OFFLINE WITH ROLLBACK IMMEDIATE;`" -Database `"master`"" 

        $dbchange >> $outputfile
        $robocpy >> $outputfile

        "Invoke-Sqlcmd -Query `"ALTER DATABASE ["+$db_build.Name+"] SET ONLINE;`" -Database `"master`""  >> $outputfile
    }
}

Đầu ra sẽ là một tập lệnh FileMover.ps1 trong thư mục MyDocument của bạn trông giống như thế này:

    Add-PSSnapin SqlServerCmdletSnapin100
    Add-PSSnapin SqlServerProviderSnapin100
    Import-Module SQLPS
    [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') "localhost" | out-null
    $server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server 
    #----------------------------------------------------------------------
    $db=$server.Databases["AdventureWorks2012"]
    $db.FileGroups["PRIMARY"].Files["AdventureWorks2012_Data"].Filename="X:\NewDBLocation\AdventureWorks2012_Data.mdf"
    $db.LogFiles["AdventureWorks2012_Log"].Filename="X:\NewDBLocation\AdventureWorks2012_log.ldf"
    $db.Alter()
    Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012] SET OFFLINE WITH ROLLBACK IMMEDIATE;" -Database "master"
    ROBOCOPY "C:\DBData" "X:\NewDBLocation" AdventureWorks2012_Data.mdf /copyall /mov
    ROBOCOPY "C:\DBFiles\Log" "X:\NewDBLocation" AdventureWorks2012_log.ldf /copyall /mov
    Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012] SET ONLINE;" -Database "master"
    #----------------------------------------------------------------------
    $db=$server.Databases["AdventureWorks2012DW"]
    $db.FileGroups["PRIMARY"].Files["AdventureWorksDW2012_Data"].Filename="X:\NewDBLocation\AdventureWorksDW2012_Data.mdf"
    $db.LogFiles["AdventureWorksDW2012_Log"].Filename="X:\NewDBLocation\AdventureWorks2012DW_log.ldf"
    $db.Alter()
    Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012DW] SET OFFLINE WITH ROLLBACK IMMEDIATE;" -Database "master"
    ROBOCOPY "C:\DBData" "X:\NewDBLocation" AdventureWorksDW2012_Data.mdf /copyall /mov
    ROBOCOPY "C:\DBData" "X:\NewDBLocation" AdventureWorks2012DW_log.ldf /copyall /mov
    Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012DW] SET ONLINE;" -Database "master"

...

Hãy cẩn thận

  • Kịch bản di chuyển tất cả các tệp, bất kể vị trí nguồn của chúng, đến cùng một đích. Bạn sẽ cần phải điều chỉnh cho các đường dẫn vị trí tùy chỉnh.
  • Kịch bản được thiết kế để chạy trên máy chủ mà bạn cần để di chuyển các tệp trên (xem tất cả các cách sử dụng localhost '). Thay thế localhost bằng tên dụ của bạn nếu bạn chạy nó từ xa.
  • Người dùng bạn chạy này khi cần truy cập vào tất cả các đường dẫn thư mục liên quan đến việc di chuyển, cả để cập nhật thông tin tên tệp của máy chủ SQL và để di chuyển các tệp.
  • Tôi sử dụng InvokeQueryCmd để thực hiện Ngoại tuyến / Trực tuyến vì tính chất thú vị của các phương thức. Set Offerline () và. SetOnline. Tôi thấy điều này đáng tin cậy hơn.

@MikeFal Tôi thấy bạn đã chấp thuận chỉnh sửa. Vì câu hỏi được gắn thẻ 2008R2 nên phần bổ sung rõ ràng hơn (đậm hoặc gì đó)? (Tôi không biết, nhưng tôi đoán nó có thể không hoạt động hoặc phá vỡ một cái gì đó trong một phiên bản khác ngoài năm 2012).
ypercubeᵀᴹ 17/03/2016

1
Tôi đã nghĩ về điều đó và tôi đã chạy nó trên SQL Server 2012 R2 - nó không xuất hiện lỗi với các lệnh ghép ngắn Add-PSSnapin SqlServerCmdletSnapin100, nhưng xử lý miễn là bạn có SQLPS Mô-đun nhập khẩu trước phần chính của tập lệnh quá trình chạy. Về mặt kỹ thuật, điều này sẽ có một số kiểm tra lỗi tốt hơn xung quanh vấn đề này, nhưng tôi đoán rằng, đây sẽ là một chỉnh sửa nhanh để giúp người khác không thể tìm thấy sự cần thiết của SQLPS Mô-đun nhập khẩu nếu họ đang ở trên phiên bản mới hơn.
Chad Rexin

1
Cảm ơn rất nhiều. Vấn đề nhỏ mặc dù. Tên tệp robocopy không được trích dẫn ở đây .. Nếu bạn có người dùng tạo tên cơ sở dữ liệu có khoảng trắng thì nó không hoạt động hoàn toàn đúng.
Tim Brigham

7

Bạn có thể sử dụng cơ sở dữ liệu Alter Sửa đổi tệp hoặc phương pháp tách / đính kèm.

Lưu ý: Cả hai sẽ yêu cầu một số thời gian chết, vì vậy phải được thực hiện trong cửa sổ bảo trì.

Điều này giả định rằng bạn có cùng cấu trúc thư mục trên ổ đĩa mới, ví dụ: C: \ data \ và D: \ Data.

- sử dụng cơ sở dữ liệu Alter với phương thức Sửa đổi (ưu tiên)

SET NOCOUNT ON

DECLARE @datafile VARCHAR(255)
    ,@logfile VARCHAR(255)
    ,@dbid TINYINT
    ,@SQLText VARCHAR(max)
    ,@dbname VARCHAR(255)
    ,@sqltext1 VARCHAR(max)
    ,@SQLText2 VARCHAR(max)

--2. Prepare for modify
IF EXISTS (
        SELECT 1
        FROM tempdb..sysobjects
        WHERE NAME LIKE '%#filetable%'
        )
BEGIN
    DROP TABLE #filetable
END

CREATE TABLE #filetable (
    mdf VARCHAR(255)
    ,ldf VARCHAR(255)
    ,dbid TINYINT
    ,dbname VARCHAR(100)
    ,fileid TINYINT
    ,logicalname SYSNAME
    )

--
INSERT #filetable (
    mdf
    ,dbid
    ,fileid
    ,logicalname
    )
SELECT physical_name
    ,database_id
    ,data_space_id
    ,NAME
FROM sys.master_files
WHERE data_space_id = 1

INSERT #filetable (
    ldf
    ,dbid
    ,fileid
    ,logicalname
    )
SELECT physical_name
    ,database_id
    ,data_space_id
    ,NAME
FROM sys.master_files
WHERE data_space_id = 0

UPDATE u
SET u.dbname = s.NAME
FROM #filetable u
INNER JOIN master..sysdatabases s ON u.dbid = s.dbid

UPDATE #filetable
SET mdf = replace(mdf, 'C:', 'D:')
    ,ldf = replace(ldf, 'C:', 'D:')
FROM #filetable

SELECT @dbid = min(dbid)
FROM #filetable
WHERE dbid > 4

WHILE @dbid IS NOT NULL
BEGIN
    SELECT @SQLText = 'alter database [' + dbname + '] MODIFY FILE (Name = ' + logicalname + ' , FileName = N''' + ldf + ''');'
    FROM #filetable
    WHERE dbid = convert(VARCHAR, @dbid)
        AND fileid = 0 -- Log file

    PRINT @SQLText

    --Exec(@SQLText)
    SELECT @SQLText2 = 'alter database [' + dbname + '] MODIFY FILE (Name = ' + logicalname + ' , FileName = N''' + mdf + ''');'
    FROM #filetable
    WHERE dbid = convert(VARCHAR, @dbid)
        AND fileid = 1 -- data file

    PRINT @SQLText2

    --Exec(@SQLText)
    SELECT @dbid = min(dbid)
    FROM #filetable
    WHERE dbid > 4
        AND dbid > @dbid
END

--- sử dụng phương pháp Old Detach / Attach (không được ưa thích, nhưng mọi người vẫn sử dụng nó .. thật không may, tôi đã sử dụng nó gần đây trên máy chủ NON prod).

DECLARE @datafile VARCHAR(255)
    ,@logfile VARCHAR(255)
    ,@dbid TINYINT
    ,@SQLText VARCHAR(8000)
    ,@dbname VARCHAR(255)
    ,@SQLText2 VARCHAR(8000)

--2. Detach All Local Databases and prepare for Attach
IF EXISTS (
        SELECT 1
        FROM tempdb..sysobjects
        WHERE NAME LIKE '%#filetable%'
        )
BEGIN
    DROP TABLE #filetable
END

CREATE TABLE #filetable (
    mdf VARCHAR(255)
    ,ldf VARCHAR(255)
    ,dbid TINYINT
    ,dbname VARCHAR(100)
    ,fileid TINYINT
    )

--
INSERT #filetable (
    mdf
    ,dbid
    ,fileid
    )
SELECT physical_name
    ,database_id
    ,data_space_id
FROM sys.master_files
WHERE data_space_id = 1

INSERT #filetable (
    ldf
    ,dbid
    ,fileid
    )
SELECT physical_name
    ,database_id
    ,data_space_id
FROM sys.master_files
WHERE data_space_id = 0

UPDATE u
SET u.dbname = s.NAME
FROM #filetable u
INNER JOIN master..sysdatabases s ON u.dbid = s.dbid

UPDATE #filetable
SET mdf = replace(mdf, 'C:', 'D:')
    ,ldf = replace(ldf, 'C:', 'D:')
FROM #filetable

SELECT @dbid = min(dbid)
FROM #filetable
WHERE dbid > 4

WHILE @dbid IS NOT NULL
BEGIN
    SELECT @SQLText = 'alter database [' + dbname + ']'
    FROM #filetable
    WHERE dbid = convert(VARCHAR, @dbid)

    SELECT @SQLText = @SQLText + CHAR(10) + ' set single_user with rollback immediate;'

    SELECT @SQLText = @SQLText + CHAR(10) + ' exec master..sp_detach_db ' + dbname
    FROM #filetable
    WHERE dbid = convert(VARCHAR, @dbid)

    PRINT @SQLText

    --Exec(@SQLText)
    SELECT @SQLText2 = 'exec master..sp_attach_db ''' + dbname + ''''
    FROM #filetable
    WHERE dbid = @dbid

    SELECT @SQLText2 = @SQLText2 + ',''' + mdf + ''''
    FROM #filetable
    WHERE dbid = @dbid
        AND mdf IS NOT NULL

    SELECT @SQLText2 = @SQLText2 + ',''' + ldf + ''''
    FROM #filetable
    WHERE dbid = @dbid
        AND ldf IS NOT NULL

    PRINT @SQLText2

    --Exec(@SQLText)
    SELECT @dbid = min(dbid)
    FROM #filetable
    WHERE dbid > 4
        AND dbid > @dbid
END

DROP TABLE #filetable

6

Cách duy nhất mà tôi biết để thực hiện nhiều DB cùng một lúc là kịch bản di chuyển cho nhiều DB cùng một lúc.

ALTER DATABASE database_nameA SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE database_nameB SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE database_nameC SET OFFLINE WITH ROLLBACK IMMEDIATE;
-------

Tại đây bạn có thể di chuyển các tệp theo cách thủ công hoặc viết một tập lệnh để thực hiện. Có thể sử dụng xp_cmdshell hoặc một số công cụ. Có lẽ dễ dàng hơn để chỉ di chuyển các tập tin bằng tay mặc dù. Đánh dấu một loạt chúng, sau đó kéo và thả.

-------
ALTER DATABASE database_nameA MODIFY FILE ( NAME = logical_name, FILENAME = 'new_path\os_file_name' );
ALTER DATABASE database_nameB MODIFY FILE ( NAME = logical_name, FILENAME = 'new_path\os_file_name' );
ALTER DATABASE database_nameC MODIFY FILE ( NAME = logical_name, FILENAME = 'new_path\os_file_name' );

ALTER DATABASE database_nameA SET ONLINE;
ALTER DATABASE database_nameB SET ONLINE;
ALTER DATABASE database_nameC SET ONLINE;

Tất nhiên, nếu bạn di chuyển tệp dữ liệu và tệp nhật ký, bạn phải đảm bảo rằng bạn thực hiện phần MODIFY FILE cho mỗi phần.


0
------------------------------
--erezbensimon@gmail.com - July 2016


use master;
go 

SET NOCOUNT ON

print '----------------------------------------------------------------------------------'
print '--Script for Moving Multiple database files to a new drive / ' + CONVERT(varchar(256),getdate() )
print '----------------------------------------------------------------------------------'
print ''


DECLARE @dbname nvarchar(128)
DECLARE @DestPath nvarchar(256)


--Set here the new destination path of the file
set @DestPath =  'T:\Data\'

------------------------------------------------
--Filter: HD Databases
------------------------------------------------
DECLARE DBList_cursor CURSOR FOR 

Select name from sys.databases

--where name like '<FIlter Something>'

----------------------------------------------

OPEN DBList_cursor

FETCH NEXT FROM DBList_cursor 
 INTO @dbname

WHILE @@FETCH_STATUS = 0
BEGIN

  declare @output_script varchar(max) --Output of the generated script
  declare @mdf_orig_path nvarchar(256) --Original datbase file path
  declare @cmdstring nvarchar(256) --Command String
  declare @CursorDeclare varchar(max) --Cursor declaration command
  declare @Originalfilename varchar(max) -- local @CursorDeclare command
  declare @filename varchar(max) -- local @CursorDeclare command
  declare @LogicalFileaame varchar(max) -- Logical FileName

  --Set null into @output script
  set @output_script=''
  --Generate Databse Cursor declaration command
  set @CursorDeclare='DECLARE DBFiles_cursor CURSOR FOR select [filename], [name] from '+ @dbname + '.sys.sysfiles'
  --Cursor Declaration
  execute (@CursorDeclare) 


  OPEN DBFiles_cursor
  FETCH NEXT FROM DBFiles_cursor INTO @filename, @LogicalFileaame

  --For RollBack Option
  select @Originalfilename = @filename

  --Modify Physical FileName
  if (@filename like '%.mdf') begin 
         select @mdf_orig_path = @filename
          IF(CHARINDEX('\', @filename) > 0)
          select @filename = RIGHT(@filename, CHARINDEX('\', REVERSE(@filename)) -1) 

         select @filename = @DestPath + @filename
         select @cmdstring = ' ''copy' + ' ' + '"'+ @mdf_orig_path + '"' + ' ' + '"' + @filename +'"' + ''''
  --Get Logical FileNAme


  end




print CHAR(10)
print '-----------------------------------------'
print  @dbname 
print '-----------------------------------------'
print CHAR(10)
print 'print  ''Start'' + CONVERT(varchar(256), getdate() ) '
print '---Offline Database' + @dbname      
print 'ALTER DATABASE ' + @dbname + ' SET OFFLINE WITH ROLLBACK IMMEDIATE' + CHAR(10) + 'GO'
print 'exec master..xp_cmdshell' + ' ' + @cmdstring + CHAR(10)
print '--For RollBack Use this:ALTER DATABASE ' + @dbname +' MODIFY FILE ( NAME =' + @LogicalFileaame +', FILENAME =' + @Originalfilename + ')' + CHAR(10)
print 'ALTER DATABASE ' + @dbname +' MODIFY FILE ( NAME =' + @LogicalFileaame +', FILENAME =' + '''' + @DestPath + @dbname + '.mdf'' )' +CHAR(10)


print '---ONline Database' + @dbname
print 'ALTER DATABASE ' + @dbname + ' SET ONLINE WITH
ROLLBACK IMMEDIATE
GO' 

  WHILE @@FETCH_STATUS = 0
  BEGIN   
   set @output_script=@output_script+' (FILENAME = '''+ @filename +'''),' 
      FETCH NEXT FROM DBFiles_cursor INTO @filename, @LogicalFileaame
  END

set @output_script=SUBSTRING(@output_script,0,len(@output_script))

CLOSE DBFiles_cursor
DEALLOCATE DBFiles_cursor

FETCH NEXT FROM DBList_cursor 
INTO @dbname

END 


CLOSE DBList_cursor
DEALLOCATE DBList_cursor

Bạn nên định dạng mã của mình (=> dba.stackexchange.com/help/formatted ) và thêm một số từ để giải thích cách đặt câu hỏi ... (=> dba.stackexchange.com/help/how-to-answer )
Julien Vavasseur

0

Kịch bản lệnh này sẽ trả về một loạt các câu lệnh mà bạn có thể chạy.

SELECT d.name as db, f.name, physical_name, f.state_desc,
'ALTER DATABASE ['+d.name+'] MODIFY FILE (name='''+f.name+''' ,filename='''+replace(physical_name,'C:\database','D:\whatever')+'''); ' as DetachCommand,
'ALTER DATABASE ['+d.name+'] SET ONLINE' as ReattachCommand
from sys.master_files f 
inner join sys.databases d on d.database_id=f.database_id
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.