数据库重定位,分离/附加数据库,MS-SQL-Server-Management-Studio(2012)

时间:2014-05-19 13:52:41

标签: sql sql-server

在接下来的一周,我们希望将数据库从一台服务器重新定位到另一台服务器。 在http://msdn.microsoft.com/en-us/library/ms187858%28v=sql.110%29.aspx上 我读到了从旧位置分离数据库并将其附加到新位置。 问题是,我无法访问服务器的文件系统,我甚至不知道服务器的确切位置^^
有没有办法将数据库从一个服务器重定位到另一个服务器,而无需访问旧服务器的文件系统?

1 个答案:

答案 0 :(得分:1)

您可以使用SQL Server中的“导入/导出”工具直接复制数据,这将在目标位置创建新数据库。关于这一点的好处是新数据库将按照您的预期工作,因为它是在目标服务器上从头开始创建的,但这也意味着您可能在存储过程或函数中使用旧的,不推荐使用的语法或者其他任何获胜的语法。除非你降低兼容性水平(尽管这不应该很难),否则不起作用。还要注意任何可能的排序规则冲突(您的旧服务器可能有SQL_Latin1_General_CP1_CI_AS,而新服务器可能是Latin1_General_CI_AS,这可能会导致平等操作失败等等。

另外,如果你有一个大型数据库,那么它需要很长时间,但是我不能想到任何其他不需要某种程度的方法。访问文件系统,因为您仍然需要到文件系统获取备份副本,或者如果使用UNC路径进行备份,源服务器需要能够写入该位置并且您之后需要能够访问它。如果其他人能够想到一个我感兴趣的人,因为隐藏起来将是一个有用的知识。

编辑:

还应该提到使用Powershell和SMO - 它与使用导入/导出向导没有任何不同,但它确实允许你微调。以下是我一直用来在不同服务器上创建数据库副本(仅限架构)的PS脚本,但缺少某些方面(NCI,FKs Indeitites等),因为副本注定是只读的。您可以轻松扩展它以复制数据。

param (
    [string]$sourceServerName = $(throw "Source server name is required."),
    [string]$destServerName = $(throw "Destination server is required."),
    [string]$sourceDBName = $(throw "Source database name is required."),
    [string]$destDBName = $(throw "Destination database name is required"),
    [string]$schema = "dbo"
 )

# Add an error trap so that at the end of the script we can see if we recorded any non-fatal errors and if so then throw
# an error and return 1 so that the SQL job recognises there's been an error.
trap 
{ 
  write-output $_ 
  exit 1 
}

# Append year to destination DB name if it isn't already on the end.
$year = (Get-Date).AddYears(-6).Year

if (-Not $destDBName.EndsWith($year)) {
    $destDBName+=$year
}

# Load assemblies.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo")  | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended")  | out-null

# Set up source connection.
$sourceSrvConn = new-object Microsoft.SqlServer.Management.Common.ServerConnection
$sourceSrvConn.ServerInstance = $sourceServerName
$sourceSrvConn.LoginSecure = $false
$sourceSrvConn.Login = "MyLogin"
$sourceSrvConn.Password = "xxx"

# Set up destination connection.
$destSrvConn = new-object Microsoft.SqlServer.Management.Common.ServerConnection
$destSrvConn.ServerInstance = $destServerName
$destSrvConn.LoginSecure = $false
$destSrvConn.Login = "MyLogin"
$destSrvConn.Password = "xxx"


$sourceSrv  = New-Object Microsoft.SqlServer.Management.SMO.Server($sourceSrvConn)
$sourceDb   = New-Object ("Microsoft.SqlServer.Management.SMO.Database")
$destSrv  = New-Object Microsoft.SqlServer.Management.SMO.Server($destSrvConn)
$destDb   = New-Object ("Microsoft.SqlServer.Management.SMO.Database")
$tbl        = New-Object ("Microsoft.SqlServer.Management.SMO.Table")
$scripter   = New-Object Microsoft.SqlServer.Management.SMO.Scripter($sourceSrvConn)

# Get the database objects
$sourceDb = $sourceSrv.Databases[$sourceDbName]
$destDb = $destSrv.Databases[$destDbName]

# Test to see databases exist. Not as easy to test for servers - if you got those wrong then this will fail and throw an error
# so it's down to the user to check their values carefully.
if ($sourceDb -eq $null) {throw "Database '" + $sourceDbName + "' does not exist on server '" + $sourceServerName + "'"}
if ($destDb -eq $null) {throw "Database '" + $destDbName + "' does not exist on server '" + $destServerName + "'"}

# Get source objects.
$tbl            = $sourceDb.tables | Where-object { $_.schema -eq $schema  -and -not $_.IsSystemObject } 
$storedProcs    = $sourceDb.StoredProcedures | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject } 
$views          = $sourceDb.Views | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject } 
$udfs           = $sourceDb.UserDefinedFunctions | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject } 
$catalogs       = $sourceDb.FullTextCatalogs
$udtts          = $sourceDb.UserDefinedTableTypes | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject } 
$assemblies     = $sourceDb.Assemblies | Where-object { -not $_.IsSystemObject } 


# Set scripter options to ensure only schema is scripted
$scripter.Options.ScriptSchema  = $true;
$scripter.Options.ScriptData    = $false;

#Exclude GOs after every line
$scripter.Options.NoCommandTerminator   = $false;
$scripter.Options.ToFileOnly            = $false
$scripter.Options.AllowSystemObjects    = $false
$scripter.Options.Permissions           = $true
$scripter.Options.DriForeignKeys        = $false
$scripter.Options.SchemaQualify         = $true
$scripter.Options.AnsiFile              = $true
$scripter.Options.Indexes               = $false
$scripter.Options.DriIndexes            = $false
$scripter.Options.DriClustered          = $true
$scripter.Options.DriNonClustered       = $false
$scripter.Options.NonClusteredIndexes   = $false
$scripter.Options.ClusteredIndexes      = $true
$scripter.Options.FullTextIndexes       = $true
$scripter.Options.NoIdentities          = $true
$scripter.Options.DriPrimaryKey         = $true

$scripter.Options.EnforceScriptingOptions   = $true


$pattern = "(\b" + $sourceDBName + "\b)"
$errors = 0

function CopyObjectsToDestination($objects) {

    foreach ($o in $objects) { 

        if ($o -ne $null) {
            try {
                $script = $scripter.Script($o)

                $script = $script -replace $pattern, $destDBName
                $destDb.ExecuteNonQuery($script)
            } catch {
                #Make sure any errors are logged by the SQL job.
                $ex = $_.Exception
                $message = $o.Name + " " + (Get-Date)
                $message += "`r`n"
                #$message += $ex.message
                $ex = $ex.InnerException

                while ($ex.InnerException) {
                    $message += "`n$ex.InnerException.message"
                    $ex = $ex.InnerException

                }
                #Write-Error $o.Name 
                Write-Error $message    # Write to caller. SQL Agent will display this (or at least some of it) in the job step history.
                # Need to use Set-Variable or changes to the variable will only be in scope within the function and we want to persist this.
                if ($errors -eq 0) {
                    Set-Variable -Name errors -Scope 1 -Value 1
                }
            }
        }
    }
}

# Output the scripts
CopyObjectsToDestination $assemblies
CopyObjectsToDestination $tbl 
CopyObjectsToDestination $udfs
CopyObjectsToDestination $views 
CopyObjectsToDestination $storedProcs 
CopyObjectsToDestination $catalogs
CopyObjectsToDestination $udtts 

# Disconnect from databases cleanly.
$sourceSrv.ConnectionContext.Disconnect()
$destSrv.ConnectionContext.Disconnect()

# Did we encounter any non-fatal errors along the way (SQL errors and suchlike)? If yes then throw an exception which tells the
# user to check the log files.
if ($errors -eq 1) { 
    throw "Errors encountered - see log file for details"
}