使用 SMO 复制数据库和数据

Posted

技术标签:

【中文标题】使用 SMO 复制数据库和数据【英文标题】:Using SMO to copy a database and data 【发布时间】:2010-09-21 01:39:11 【问题描述】:

我正在尝试将数据库复制到同一服务器上的新数据库中。服务器是我在 Windows XP 下运行 SQL 2008 Express 的本地计算机。 使用 SMO.Transfer 类应该很容易做到这一点,而且几乎可以工作!

我的代码如下(有些简化):

Server server = new Server("server");
Database sourceDatabase = server.Databases["source database"];

Database newDatbase = new Database(server, "new name");
newDatbase.Create();

Transfer transfer = new Transfer(sourceDatabase);
transfer.CopyAllObjects = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newDatbase.Name;
transfer.CopySchema = true;
transfer.CopyData = true;
StringCollection transferScript = transfer.ScriptTransfer();

using (SqlConnection conn = new SqlConnection(connectionString))

    conn.Open();
    using (SqlCommand switchDatabase = new SqlCommand("USE " + newDatbase.Name, conn))
    
        switchDatabase.ExecuteNonQuery();
    

    foreach (string scriptLine in transferScript)
    
        using (SqlCommand scriptCmd = new SqlCommand(scriptLine, conn, transaction))
        
            int res = scriptCmd.ExecuteNonQuery();
        
    

我在这里所做的是首先创建一个新数据库,然后使用Transfer 类生成一个复制脚本,最后在新数据库中运行该脚本。

这可以很好地复制结构,但CopyData 选项不起作用!

CopyData 选项是否有任何未记录的限制?文档只说该选项指定是否复制数据。

我尝试使用 TransferData() 方法在不使用脚本的情况下复制数据库,但随后出现异常“无法连接到服务器”,内部异常显示“与网络相关或特定于实例的错误建立与 SQL Server 的连接时发生。未找到或无法访问服务器。请验证实例名称是否正确以及 SQL Server 是否配置为允许远程连接。(提供程序:命名管道提供程序,错误:40 - 无法打开与 SQL Server 的连接)"

我也尝试在服务器上启用命名管道,但这无济于事。

编辑: 我找到了一个解决方案,通过备份然后将其恢复到新数据库来工作。虽然它很笨拙,而且比它应该的要慢,所以我仍在寻找更好的解决方案。

【问题讨论】:

【参考方案1】:

好吧,在联系了 Microsft 支持后,我得到了它的正常工作,但它运行缓慢并且或多或少没用。进行备份然后还原要快得多,只要新副本与原始副本位于同一服务器上,我就会使用它。

工作代码如下:

ServerConnection conn = new ServerConnection("rune\\sql2008");
Server server = new Server(conn);

Database newdb = new Database(server, "new database");
newdb.Create();

Transfer transfer = new Transfer(server.Databases["source database"]);
transfer.CopyAllObjects = true;
transfer.CopyAllUsers = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newdb.Name;
transfer.DestinationServer = server.Name;
transfer.DestinationLoginSecure = true;
transfer.CopySchema = true;
transfer.CopyData = true;
transfer.Options.ContinueScriptingOnError = true;
transfer.TransferData();

诀窍是设置 DestinationDatabase 属性。即使目标与源相同,也必须设置此项。此外,我必须以命名实例的形式连接到服务器,而不是使用其他连接选项。

【讨论】:

备份和恢复总是会更快,因为它不受锁定开销、事务等的影响。备份的设计速度非常快。对不起,我没有早点找到你! :-D 但是运行备份可能会干扰当前的备份顺序和日志传输/截断。您可以使用 SMO 创建一个 COPY_ONLY 备份,如msdn.microsoft.com/en-us/library/ms191495.aspx 所述“ 另外,使用 C# SMO 进行备份和恢复会丢失所有用户和登录信息,不是吗? 值得注意的是,这仅在您以“sa”用户身份登录时才有效,否则当您创建数据时,您的用户被分配为 dbo,然后它尝试在目标数据库中创建一个新用户你当前的用户,你得到一个例外。即使你明确告诉它不要复制登录名、用户名或出于某种原因它想要创建当前用户的任何内容。 我使用了上面的代码,但是当我设置属性 transfer.copydata=true 时,我在“对象引用未设置为对象的实例”中遇到异常。而且没有这个属性意味着,Schema被复制没问题..如何解决这个问题【参考方案2】:

我尝试过让这个工作,并想出了一个不使用 Transfer 类的答案。这是我使用的方法:

       public bool CreateScript(string oldDatabase, string newDatabase)
   
       SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=" + newDatabase + ";User Id=sa;Password=sa;");
       try
       
           Server sv = new Server();
           Database db = sv.Databases[oldDatabase];

           Database newDatbase = new Database(sv, newDatabase);
           newDatbase.Create(); 

           ScriptingOptions options = new ScriptingOptions();
           StringBuilder sb = new StringBuilder();
           options.ScriptData = true;
           options.ScriptDrops = false;
           options.ScriptSchema = true;
           options.EnforceScriptingOptions = true;
           options.Indexes = true;
           options.IncludeHeaders = true;
           options.WithDependencies = true;

           TableCollection tables = db.Tables;

           conn.Open();
           foreach (Table mytable in tables)
           
               foreach (string line in db.Tables[mytable.Name].EnumScript(options))
               
                   sb.Append(line + "\r\n");
               
           
           string[] splitter = new string[]  "\r\nGO\r\n" ;
           string[] commandTexts = sb.ToString().Split(splitter, StringSplitOptions.RemoveEmptyEntries);
           foreach (string command in commandTexts)
           
               SqlCommand comm = new SqlCommand(command, conn);
               comm.ExecuteNonQuery();
           
           return true;
       
       catch (Exception e)
       
           System.Diagnostics.Debug.WriteLine("PROGRAM FAILED: " + e.Message);
           return false;
       
       finally
       
           conn.Close();
       
   

【讨论】:

在 sb.append(line+"\r\n") 上出现错误,“对象引用未设置为对象的实例” @franchescototti:不,你没有。除非您对导致 sb 为空的代码进行了修改,否则您不会收到该错误。【参考方案3】:

尝试在 Server 对象上将 SetDefaultInitFields 设置为 true。

SMO 数据库对象运行缓慢时遇到了同样的问题。我猜这是因为 sql server 不喜欢一次检索整个对象和集合,而是延迟加载所有内容,导致每个字段往返,这对于整个数据库来说效率非常低。

【讨论】:

【参考方案4】:

这是我的解决方案:

    我有一个名为 Olddatabase 的数据库

    我把它备份到 E:\databackup\Old.bak

    如果您想在同一台服务器上创建一个名为 NewDatabase 的 Olddatabase 重复数据库

3.1 您可以在查询工具中使用命令:EXEC OldDatabase.dbo.sp_helpfile; 如果您想将 NewDatabase 保存在同一文件夹中,则确定 OldDatabase 的路径。

或者您可以将 NewDatabase 保存在您想要的新路径中

    在查询工具中使用该命令

    从磁盘恢复数据库 NewDatabase = 'E:\databackup\Old.bak' WITH MOVE 'OldDatabase' TO 'E:\New path (or the same path)\NewDatabase_Data.mdf', MOVE 'OldDatabase_log' TO 'E:\New 路径(或相同路径)\NewDatabase_Log.ldf';

注意:您可以在 c# 中使用上述命令:在 sql 中创建一个包含上述命令的存储过程。您可以在 C# 中调用存储过程

【讨论】:

以上是关于使用 SMO 复制数据库和数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中使用 SMO 使用 FILE STREAM 备份和恢复数据库

使用PowerShell和SMO库从.BAK文件创建新数据库

使用 smo 和 powershell 将数据库备份还原到新数据库时出错

将 SMO 数据类型转换为 SqlDataTypes

使用 SMO 更改逻辑数据库名称

sql 使用PowerShell和SMO为给定数据库中的MS SQL存储过程和视图创建drop + create脚本