2017-12-18 4 views
1

ベンダーからのCSVファイルを取得してローカルデータベースに格納するSSISパッケージがいくつかあります。私が抱えている問題は、ベンダーが列を追加または削除することがあり、次回の実行前にパッケージを更新する時間がないため、SSISパッケージが異常終了することです。私は何とかこれが起こるのを防ぐためにしたい。ベンダーが列を追加/削除できるときにCSVをデータベースにロード

CSVファイルを1行ずつ読み込み、新しい列を取り除いてから、変更した行をテーブルに挿入するのに、現在のプロセス(CSVファイル数千または数十万のレコードを持つことができます)。

私はADO接続の使用について検討し始めましたが、ローカルマシンにはACEプロバイダもJETプロバイダもなく、パッケージが展開されるサーバーにもプロバイダがないと思います。デプロイメントサーバー)。

テーブルを読み込み、新しく追加または削除されたカラムを無視できるようにするためにできることは紛失しています(ただし、CSVファイルにテーブルのカラムがない場合は、迅速かつ信頼性の高い方法です。何か案は?

+0

どのようにCSVを1行ずつ読みましたか?あなたはどのようにインサートをしましたか?どのコンポーネントを使用しましたか? –

+0

私はファイルを読むためにcsvreaderを使いました。挿入はsqlcommandオブジェクトを介して行われました。 –

+0

あなたがcsvreaderと言うときは、フラットファイルソースを意味しますか、これはいくつかの顧客コンポーネントです。 sqlcommandオブジェクトは、行単位の挿入を行います。これをoledbデスティネーションに変更すると、フラットファイルソース –

答えて

1

私は別のアプローチで行っていましたが、これは動作しているようです(いくつかのキンクを見つけた後)。私がしたのは、CSVファイルの行を取り出し、一時的なデータテーブルに入れることでした。それが完了したら、データテーブルからデータベースに一括コピーを行いました。欠落した列や新しい列に対処するために、私はどの列がCSVと表に共通しているかを判断し、それらの共通列のみを処理しました(新しい列はログファイルに記録されているため、後で追加できます)。

Private Sub BulkCopy(csvFile As String) 
    Dim i As Integer 
    Dim rowCount As Int32 = 0 
    Dim colCount As Int32 = 0 
    Dim writeThis As ArrayList = New ArrayList 

    tempTable = New DataTable() 
    Try 
     '1) Set up the columns in the temporary data table, using commonColumns 

     For i = 0 To commonColumns.Count - 1 
      tempTable.Columns.Add(New DataColumn(commonColumns(i).ToString)) 
      tempTable.Columns(i).DataType = GetDataType(commonColumns(i).ToString) 
     Next 

     '2) Start adding data from the csv file to the temporary data table 

     While Not csvReader.EndOfData 
      currentRow = csvReader.ReadFields() 'Read the next row of the csv file 
      rowCount += 1 
      writeThis.Clear() 

      For index = 0 To UBound(currentRow) 
       If commonColumns.Contains(csvColumns(index)) Then 
        Dim location As Integer = tableColumns.IndexOf(csvColumns(index)) 
        Dim columnType As String = tableColumnTypes(location).ToString 

        If currentRow(index).Length = 0 Then 
         writeThis.Add(DBNull.Value) 
        Else 
         writeThis.Add(currentRow(index)) 
        End If 
        'End Select 
       End If 
      Next 

      Dim row As DataRow = tempTable.NewRow() 
      row.ItemArray = writeThis.ToArray 
      tempTable.Rows.Add(row) 
     End While 
     csvReader.Close() 

     '3) Bulk copy the temporary data table to the database table. 

     Using copy As New SqlBulkCopy(dbConnection) 
      '3.1) Set up the column mappings 
      For i = 0 To commonColumns.Count - 1 
       copy.ColumnMappings.Add(commonColumns(i).ToString, commonColumns(i).ToString) 
      Next 

      '3.2) Set the destination table name 
      copy.DestinationTableName = tableName 

      '3.3) Copy the temporary data table to the database table 
      copy.WriteToServer(tempTable) 

     End Using 
    Catch ex As Exception 
     message = "*****ERROR*****" + vbNewLine 
     message += "BulkCopy: Encountered an exception of type " + ex.GetType.ToString() 
     message += ": " + ex.Message + vbNewLine + "***************" + vbNewLine 
     LogThis(message) 
    End Try 
End Sub 

そこよりエレガントな何かがあるかもしれませんが、これは、これまでに動作するようです:ここに私のバルク・コピー・モジュールです。

0

実行時にメタデータに基づいて動的にSSISパッケージをビルドして実行するBiMLを調べます。

0

このコメントに基づいて:

私は新しい 列を除去、線でCSVファイルの行に読んでみました、その後、テーブルに変更された行 を置くためにINSERT文を使用してきました、それは現在のプロセスである (CSVファイルには数千から数十万件の レコードが含まれています)よりもはるかに時間がかかります。

そして、この:

私は、ファイルを読むためにcsvreaderを使用。挿入は、sqlコマンド オブジェクトを介して行われました。

ボトルネックは、フラットファイルのソースではなく宛先に表示されます。 OLEDBコマンドは行ごとに実行され、入力行ごとに1つの文が実行されます。これをOLEDB宛先に変更すると、プロセスがバルク挿入操作に変換されます。これをテストするには、フラットファイルソースを使用し、それを派生列に接続します。それを実行し、速度を確認してください。早ければ、oledbの宛先に変更して、もう一度やり直してください。また、ヒープ(クラスタ化されていないインデックスまたは非クラスタ化されたインデックスなし)に挿入し、タブロックを使用することもできます。

しかし、これはファイル全体のさまざまな問題を解決するものではありません。私はフラットファイルのソースが、列が短い場合や、設計時に最初に設定した方法より多くの場合は何をするのか分かりません。それは失敗するか、次の行の一部が現在の行の最後の列に割り当てられているギザギザの形式で行をインポートする可能性があります。それは大きな混乱になる可能性があります。

ただし、フラットファイルソースが余分な列を取得したときに何が起こるかはわかります。私は悲しそうに拒否されたこの接続項目を入れました:https://connect.microsoft.com/SQLServer/feedback/details/963631/ssis-parses-flat-files-incorrectly-when-the-source-file-contains-unexpected-extra-columns

何が起こるかは、余分な列が最後の列に連結されることです。そのために計画している場合は、最後の列を大きくしてから、ステージング表からSQLを解析することができます。また、行全体をSQLに詰め込み、そこから各列を解析することもできます。あなたがCHARINDEX()をして、すべての場所の値の位置をチェックするので、ちょっと厄介です。

より簡単なオプションは、スクリプトタスクでsplit()のコンボを使用してすべての値を取得し、配列の値の数をチェックしていくつの列があるかを調べることです。これにより、見つけたものに基づいて行を別のバッファに送ることもできます。

最後に、ベンダーにフォーマットをコミットするよう依頼することができます。固定数の列か、XMLのようなバリエーションを処理する形式を使用します。

0

ソーススクリプトコンポーネントのC#ソリューション(私はそれをチェックしていませんが、動作すると思います)があります。

splitを使用してヘッダーを配列に読み込みます。

各データ行に対して、同じ分割関数を使用し、ヘッダー値を使用して列をチェックし、rowvalを使用して出力を設定します。

すべての出力列を出力領域に配置する必要があります。

存在しないすべての列は、終了時にヌル値を持ちます。

public override void CreateNewOutputRows() 
    { 


     using (System.IO.StreamReader sr = new System.IO.StreamReader(@"[filepath and name]")) 
     { 
      while (!sr.EndOfStream) 
      { 
       string FullText = sr.ReadToEnd().ToString(); 
       string[] rows = FullText.Split('\n'); 

       //Get header values 
       string[] header = rows[0].Split(','); 


       for (int i = 1; i < rows.Length - 1; i++) 
       { 
        string[] rowVals = rows[i].Split(','); 
        for (int j = 0; j < rowVals.Length - 1; j++) 
        { 

         Output0Buffer.AddRow(); 
         //Deal with each known header name 
         switch (header[j]) 
         { 
          case "Field 1 Name": //this is where you use known column names 
           Output0Buffer.FieldOneName = rowVals[j]; //Cast if not string 
           break; 
          case "Field 2 Name": 
           Output0Buffer.FieldTwoName = rowVals[j]; //Cast if not string 
           break; 
          //continue this pattern for all column names 
         } 
        } 

       } 
      } 
     } 


    } 
関連する問題