1

これは、私の最初の質問「“SQL” エクスポートを T-SQL に移植する 」のフォローアップです。

私は、自分では制御できず、変更できないサードパーティのプログラムを使用しています。このプログラムは、内部データベースを.sql次の形式でそれぞれのセットにエクスポートします。

INSERT INTO [ExampleDB] ( [IntField] , [VarcharField], [BinaryField])
VALUES
(1 , 'Some Text' , 0x123456),
(2 , 'B' , NULL),
--(SNIP, it does this for 1000 records)
(999, 'E' , null);
(1000 , 'F' , null);

INSERT INTO [ExampleDB] ( [IntField] ,  [VarcharField] , BinaryField)
VALUES
(1001 , 'asdg', null),
(1002 , 'asdf' , 0xdeadbeef),
(1003 , 'dfghdfhg' , null),
(1004 , 'sfdhsdhdshd' , null),
--(SNIP 1000 more lines)

このパターンは、.sqlファイルがエクスポート中に設定されたファイル サイズに達するまで続きます。エクスポート ファイルは次のようにグループ化されますEXPORT_PATH\%Table_Name%\Export#.sql。# は 1 から始まるカウンターです。

現在、約 1.3GB のデータがあり、1MB のチャンクでエクスポートしています (26 個のテーブルにまたがる 1407 個のファイル、5 個を除くすべてのテーブルには 1 個のファイルしかなく、最大のテーブルには 207 個のファイルがあります)。

現在、各ファイルを ram に読み込み、 ExecuteNonQueryを呼び出す単純な C# プログラムがあります。問題は、平均 60 秒/ファイルです。つまり、エクスポート全体を実行するには約 23 時間かかります。

INSERT INTO の代わりに BULK INSERT でロードされるようにファイルをフォーマットする方法があれば、はるかに高速になると思います。これを行う簡単な方法はありますか、それともある種の検索と置換を作成し、いくつかのコーナーケースで失敗してデータを爆破しないように指を交差させておく必要がありますか?

挿入を高速化する方法に関する他の提案もいただければ幸いです。


アップデート:

私は解析に行き、 SqlBulkCopy メソッドを実行しました。1ファイル/分からでした。~1ファイル/秒。

4

2 に答える 2

1

どうやら、データは常に括弧で囲まれ、左括弧で始まります。このルールを使用して、これらの各行をsplit( RemoveEmptyEntries) し、それを DataTable にロードすることができます。SqlBulkCopyその後、データベースに一度にコピーするために使用できます。

このアプローチは必ずしもフェイルセーフではありませんが、確実に高速になります。

編集:すべてのテーブルのスキーマを取得する方法は次のとおりです。

private static DataTable extractSchemaTable(IEnumerable<String> lines)
{
    DataTable schema = null;
    var insertLine = lines.SkipWhile(l => !l.StartsWith("INSERT INTO [")).Take(1).First();
    var startIndex = insertLine.IndexOf("INSERT INTO [") + "INSERT INTO [".Length;
    var endIndex = insertLine.IndexOf("]", startIndex);
    var tableName = insertLine.Substring(startIndex, endIndex - startIndex);
    using (var con = new SqlConnection("CONNECTION"))
    {
        using (var schemaCommand = new SqlCommand("SELECT * FROM " tableName, con))
        {
            con.Open();
            using (var reader = schemaCommand.ExecuteReader(CommandBehavior.SchemaOnly))
            {
                schema = reader.GetSchemaTable();
            }
        }
    }
    return schema;
}

次に、ファイル内の各行を繰り返し、それが で始まるかどうかを確認し、(その行を で分割するだけSplit(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)です。次に、結果の配列を作成したスキーマ テーブルに追加できます。

このようなもの:

var allLines = System.IO.File.ReadAllLines(path);
DataTable result = extractSchemaTable(allLines);
for (int i = 0; i < allLines.Length; i++)
{
    String line = allLines[i];
    if (line.StartsWith("("))
    {
        String data = line.Substring(1, line.Length - (line.Length - line.LastIndexOf(")")) - 1);
        var fields = data.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        // you might need to parse it to correct DataColumn.DataType
        result.Rows.Add(fields);
    }
}
于 2012-04-03T22:43:50.103 に答える
1

さて、ここにデータを DataTable に変換するのを助けるための私の「解決策」があります(LINQPadで実行します):

var i = "(null, 1 , 'Some''\n Text' , 0x123.456)";
var pat = @",?\s*(?:(?<n>null)|(?<w>[\w.]+)|'(?<s>.*)'(?!'))";
Regex.Matches(i, pat,
      RegexOptions.IgnoreCase | RegexOptions.Singleline).Dump();

一致は、値グループごとに 1 回実行する必要があります (例: (a,b,etc))。結果の解析 (変換など) は呼び出し元に委ねられており、[あまり] テストしていません。最初に正しく型指定された DataTable を作成することをお勧めします-- すべてを「文字列として」データベースに渡すことは可能かもしれませんが? -- 次に、列の情報を使用して抽出プロセスを支援します (おそらく型コンバーターを使用します)。キャプチャの場合: nnull、w単語 (数字など)、s文字列です。

ハッピーコーディング。

于 2012-04-03T23:19:13.590 に答える