2

メモリの問題により、DataTable を IDataReader に置き換える/変換することにしました。

かなりの時間 Google と MSDN で検索した後、http://www.michaelbowersox.com/2011/12/22/using-a-custom-idatareader-to-stream-data-into-a-databaseでこれを見つけました。 /およびSql Serverの何百万ものレコードを一括挿入します

私はLumenWorks Fast CSV Readerを使用しているため、CsvReaderにIDataReaderに2つの異なるフィールドバージョンを使用させる方法をまだ理解していません。:-( csvReader.FieldCount がここで重要ですが、IDataReader インターフェイスを持つ 2 つの新しいクラスのいずれかを使用するように CsvReader に指示する方法がわかりません。以下の元のスクリプトと変更されたスクリプトを参照してください...ありがとう...

//元のスクリプト...

var dbConnection = new SqlConnection(_dbConnectionString);

using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
   using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly))
   {
       while(csvReader.ReadNextRecord())
       {
           if (csvReader.FieldCount == 48)
           {
               //Version 1...
               dataRow["DealerId"] = csvReader[0];
               dataRow["DealerName"] = csvReader[1];
               //Etc...
           }
           else if (csvReader.FieldCount == 51)
           {
               //Version 2...
               dataRow["DealerId"] = csvReader[0];
               dataRow["DealerName"] = csvReader[1];
               //Etc...
           }
           else { throw new Exception("Field Total Count Mismatched"); }

           dataTable.Rows.Add(dataRow);
       }

       dbConnection.Open();

       dbBulkCopy.WriteToServer(dataTable);
   }
}

// 新しいスクリプト...

 var dbConnection = new SqlConnection(_dbConnectionString);

using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
   dbConnection.Open();

   using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly))
   {
       csvReader.ReadNextRecord();

       dbBulkCopy.WriteToServer(
           if (csvReader.FieldCount == 48)
           {
               //Version 1...

               csvReader....???  //Assign a custom class having IDataTable...
           }
           else if (csvReader.FieldCount == 51)
           {
               //Version 2...
               csvReader....???  //Assign a custom class having IDataTable...
           }
           else { throw new Exception("Field Total Count Mismatched"); }
        );
   }
}

//サンプルスクリプト...

using (var file = new StreamReader(path))
using (var csv = new CsvReader(file, true)) // true = has header row
using (var bcp = new SqlBulkCopy(connection)) {
    bcp.DestinationTableName = "TableName";
    bcp.WriteToServer(csv);
}
4

1 に答える 1

1

少し長くなるので、回答として書きます。

* フィールドの順序が異なる2種類のcsvファイルがあるにもかかわらず、ターゲットテーブルは同じであると想定しています

あなたの文脈を理解できるようにするために、ここからいくつかのサンプルデータを入手しました。

最初のタイプのファイルは次のようなものだとしましょう:

Rk,Year,Age,Tm,Lg,Id,G,W,L,W-L,Finish,
1,1978,37,Atlanta Braves,NL,,162,69,93,.426,6

そして2番目のタイプのような(一部の列は逆年齢<->終了であり、追加のフィールドがあります)

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3
1,1978,Very good year,Atlanta Braves,NL,,162,69,93,.426,96,,,,

したがって、ターゲットテーブルは次のようになります(列のみ)

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3

ここに 2 つのオプション(最後に +1 オプション)があります。

オプション1

  1. ステップ 0 を追加して、フィールド フォーマットを定義することにより、すべての入力ファイルをフィールド レベルで統一します。これは、データベースにあるのと同じフィールドを作成することで実行できます。

[Test4 と Test5 がターゲット テーブルに存在するが、両方の CSV ファイルにない列であるとします]

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3,Test4,Test5
  1. あなたが持っているすべてのファイル(両方のタイプ)を解析し、定義したフォーマットを尊重して、それらを1つ(またはあなた次第の複数)に書き換えます。このようにして、一意の形式を持つ 1 つ (または複数) のファイルのみが作成されます。

  2. フィールドの非互換性の問題は、一意の形式で取得した最後のファイルで処理されるため、csv リーダーを使用してこのファイルを解析してデータベースに挿入できます。

オプション 2

SqlBulkCopy 操作を 2 回実行します。最初のラウンドでは 48 フィールドを持つファイルを読み取り、次のラウンドでは 51 フィールドを持つファイルを読み取ります。

            var FilesWith48Fields = Directory.GetFiles(@"D:\Data\48Fields", "*.csv");

            foreach (var fileName in FilesWith48Fields)
            {
                using (var file = new StreamReader(fileName))
                using (var csv = new CsvReader(file, true)) // true = has header row
                using (var bcp = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls))
                {
                    bcp.DestinationTableName = "fletchsodTable";
                    // map the first field of your CSV to the column in Database
                    var mapping1 = new SqlBulkCopyColumnMapping(0, "FirstColumnName");
                    bcp.ColumnMappings.Add(mapping1);

                    var mapping2 = new SqlBulkCopyColumnMapping(1, "SecondColumnName");
                    bcp.ColumnMappings.Add(mapping2);  
                    ....

                    bcp.WriteToServer(csv);
                }
            }

そして、51フィールドを持つファイルで同じことを繰り返します

var FilesWith51Fields = Directory.GetFiles(@"D:\Data\51Fields", "*.csv");
......

SqlBulkCopyColumnMapping の詳細については、こちらを参照してください。

オプション 3

データ リーダーの作成を冒険したい場合は、次のリンクを参照してください。

Daniel Wertheim のブログ

codeproject の実装例

もう一つ

最後にMSDN

個人的なメモ 時間がないため、私の同様の問題について、3番目のオプションを断念しました。これは、実行する必要があるすべての単体テストと最適化、および別の調整により、時間がかかる可能性があるためです(少なくとも私にとっては、ケース

オプション 4 おそらく、オプション 2 で指摘した列マッピングを使用して、フィールド数をテストすることで独自の方法を実装することをお勧めします。しかし本能的に、ハードコードされた整数でフィールドをカウントしないことをお勧めします。

于 2013-07-24T00:09:58.167 に答える