使用 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文件创建新数据库