web-dev-qa-db-ja.com

SMOを使用してデータベースとデータをコピーする

同じサーバー上の新しいデータベースにデータベースのコピーを作成しようとしています。サーバーは、WindowsXPでSQL2008Expressを実行しているローカルコンピューターです。これを行うには、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がリモート接続を許可するように構成されていることを確認してください(プロバイダー:Named Pipes Provider、エラー:40-可能性があります) SQL Serverへの接続を開かない)」

また、サーバーで名前付きパイプを有効にしようとしましたが、それは役に立ちません。

編集:バックアップを作成してから新しいデータベースに復元することで機能するソリューションを見つけました。しかし、それはかなり不器用で、本来よりも遅いので、私はまだより良い解決策を探しています。

22
Rune Grimstad

さて、Microsft Supportに連絡した後、私はそれを適切に動作させましたが、それは遅く、多かれ少なかれ役に立たないです。バックアップを実行してから復元する方がはるかに高速であり、新しいコピーが元のコピーと同じサーバー上にある限り、それを使用します。

作業コードは次のとおりです。

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プロパティを設定することでした。ターゲットがソースと同じであっても、これを設定する必要があります。さらに、他の接続オプションを使用する代わりに、名前付きインスタンスとしてサーバーに接続する必要がありました。

17
Rune Grimstad

私はこれを機能させることに挑戦し、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();
       }
   }
3
emy

サーバーSetDefaultInitFields をtrueに設定してみてください)オブジェクト。

SMOデータベースオブジェクトの実行速度が遅い場合にも同じ問題が発生しました。これは、SQLサーバーがオブジェクトとコレクション全体を一度に取得することを好まないためだと思います。代わりに、すべてを遅延読み込みして、各フィールドのラウンドトリップを引き起こします。これは、データベース全体ではかなり非効率的です。

2
Rob

これが私の解決策です:

  1. Olddatabaseという名前のデータベースがあります
  2. E:\ databackup\Old.bakにバックアップします

  3. NewDatabaseという名前の同じサーバー内のOlddatabaseから重複データベースを作成する場合

3.1クエリツールでコマンドを使用できます:EXEC OldDatabase.dbo.sp_helpfile; NewDatabaseを同じフォルダに保存したい場合に備えて、OldDatabaseのパスを決定するために保存されます。

または、NewDatabaseを必要な新しいパスに保存できます

  1. クエリツールでこのコマンドを使用します

    RESTORE DATABASE NewDatabase FROM DISK = 'E:\ databackup\Old.bak' WITH MOVE'OldDatabase 'TO'E:\ New path(または同じパス)\ NewDatabase_Data.mdf'、MOVE'OldDatabase_log 'TO'E:\ Newパス(または同じパス)\ NewDatabase_Log.ldf ';

注:c#でこれらのコマンドを使用するには、次のコマンドを使用します。上記のコマンドを含むストアドプロシージャをSQLで作成します。そして、C#でストアドプロシージャを呼び出すことができます

1
emcuapharaon