2016-05-27 9 views
1

SSISパッケージを使用してSQL Serverにインポートする必要がある毎月のXLSXファイルを受け取ります。残念ながら、送信者はUNCのファイル名またはワークシートの名前付けに従わず、最近のSQL Server 2012への移行により、Excel Connection Managerを使用している場合でもパッケージが失敗しました。また、テンプレートを送信しようとしましたが、そのテンプレートに従うことを拒否しており、強制的にテンプレートを使用することはできません。xlsxファイルをSQL Serverにインポートする際の問題

スクリプトタスクを使用して2つのExcelワークシートのそれぞれをSystem.Objectにインポートするパッケージを更新しようとしていますが、これをクエリまたはループスルーしてデータを出力先SQL Serverテーブル

これまでMicrosoft hereの例を使用して、Excelファイルパス/名前と両方のワークシート名をObject変数にインポートできました。ただし、ワークシートからの実際のデータセットを含むObjectは作成されません。

ここでの例とWebの他の箇所に基づいて、ワークシートデータをObject変数に出力すると思われるC#スクリプトを開始しましたが、C#ではあまり堪能ではなく、コピー元の完全な例。これは、これまでの私のコードです:

using System; 
using System.Data; 
using System.Data.OleDb; 
using Microsoft.SqlServer.Dts.Runtime; 
using System.Windows.Forms; 

[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute] 
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase 
{ 
     public DataSet Main() 
     { 
      string fileName; 
      string connectionString; 

      fileName = Dts.Variables["ExcelFile"].Value.ToString(); 
      Console.WriteLine(fileName); 

      connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;" + 
       "Data Source=" + fileName + ";Extended Properties=Excel 12.0 Xml"; 
      Console.WriteLine(connectionString);   
      DataSet data = new DataSet(); 
      using (OleDbConnection con = new OleDbConnection(connectionString)) 
      { 
       con.Open(); 
       OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", connectionString); 
       adapter.Fill(data); 
      } 

      return data; 
     } 
} 

コードは正常にビルドが、私は、私は目立たエラー

Error: 0x1 at Script Task: Exception has been thrown by the target of an invocation.
Task failed: Script Task

を受けるパッケージを実行すると、私は私のConsole.WriteLineコマンドのいずれかから任意の出力を得ることはありません、私はScript Taskがすぐに失敗すると思います。 Delay Validation = True、それを変更しても差は出ませんでした。あなたのスクリプトに明白な/初心者のエラーが表示されますか?私は何年もの間SQLとSSISを使っていましたが、私のC#/ VB/Java/etc。知識と経験は限られています。

また、私がSSIS(これは動作しないExcel Connection以外)でこれを達成するためのより良い方法を見落としている場合は、教えてください。

UPDATE - 5/31/16:今日、プロジェクトに取り掛かる時間が少しありました。少し進歩しました。私は、以下を含むために私のスクリプトタスクを更新しました:

 DataSet data = new DataSet(); 
     using (OleDbConnection con = new OleDbConnection(connectionString)) 
     { 
      con.Open(); 
      OleDbDataAdapter adapter = new OleDbDataAdapter(query, con); 
      //OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM [Indemnity Scores$]", con); 
      adapter.Fill(data); 
      Dts.Variables["ExcelDataTable_IndemnityScores"].Value = data; 
     } 

スクリプトタスクは現在正常に完了したので、私はその後Foreachループコンテナを追加し、変数列挙子からforeachのにそれを設定し、コレクションとしてExcelDataTable_IndemnityScoresを選択しました。

しかし、今はこのObject変数からデータを抽出するのが難しいです。私は変数マッピングで設定した2つの列を持ちます(または少なくともです)。また、SQLを実行するコマンドを使用して値をテーブルに挿入しています。残念ながら、各列の空白値は1つだけ挿入されます。

次に、私はExecute SQLを単純なスクリプトタスクに置き換えて、各変数の値を返しました。残念ながら、値の代わりに "Microsoft.SqlServer.Dts.Runtime.Variable"を返します。私はこれが初心者のエラーであると推測しますが、エラーを説明するオンラインのものはまだ見つかりませんでしたか?

更新日6/14/2016: 最終的にパッケージを完成し、昨日の生産で正常に実行されました。私はここで紹介されたアドバイスと他の場所で見つかった例を使用して終了しました。私の一般的なワークフローでは、ソースワークブックから両方のワークシートをインポートするためにトリプルネストされたForeachループが必要でした - 私は1ヶ月に1回しか期待していませんが、100%はこのタスクと一貫していません。

私の最も外側のループは、単にFTPプロセスによってダウンロードされたファイルを見つけるために私のインポートディレクトリを列挙します。 2つのスクリプトタスクが含まれています。最初のものは、FTPプロセスによってダウンロードされた最初のスプレッドシートのファイル名を確認するだけです。上記のMicrosoftのリンクを自分のコードに使用しましたが、私の変数名を少し変更しました。

2番目のタスクは、すべてのワークシート名を最初のスプレッドシートから取得し、上記のMicrosoftリンクを使用して作成しました。しかし、XMLデータベースが自分の変数に代入されないように、ワークシート名に "#"を付けて除外します。

第2ループ(最初の内側ループ)は、最初のループ内で解析された各ワークシート名を列挙します。これには3つのスクリプトタスクが含まれており、そのうちの最初のワークシートからのデータをオブジェクト変数にインポートします。

公共ボイドメイン(){ このループにおける第二のスクリップタスクは単にExcelから空白行を削除 {

  string fileName; 
      string connectionString; 
      string worksheetName; 
      string query; 

      fileName = Dts.Variables["ExcelFile"].Value.ToString(); 
      //MessageBox.Show("InsertWorksheetDataIntoObject - Filename: " + fileName); 

      connectionString = String.Format("Provider=Microsoft.ACE.OLEDB.12.0;" + 
       "Data Source={0};Extended Properties=Excel 12.0 Xml;", fileName); 
      //MessageBox.Show("Connection: " + connectionString); 

      worksheetName = Dts.Variables["ExcelTable"].Value.ToString(); 
      worksheetName = worksheetName.Replace("'", ""); 
      //MessageBox.Show("InsertWorksheetDataIntoObject - Worksheet: " + worksheetName); 

      query = string.Format("SELECT * FROM [" + worksheetName + "]"); 
      //MessageBox.Show("Query: " + query); 

      DataSet data = new DataSet(); 
      using (OleDbConnection con = new OleDbConnection(connectionString)) 
      { 
       con.Open(); 
       OleDbDataAdapter adapter = new OleDbDataAdapter(query, con); 
       adapter.Fill(data); 
       Dts.Variables["ExcelDataTable"].Value = data; 
      } 

      Dts.TaskResult = (int)ScriptResults.Success; 
     } 

     catch (Exception ex) 
     { 
      Dts.Events.FireError(-1, "ErrorMessage", ex.ToString(), "", 0); 
      Dts.TaskResult = (int)ScriptResults.Failure; 
     } 


     //return data; 

    } 

を試みます。私は上記のスクリプトでそれを組み込むことができたかもしれませんが、将来のどこかで再利用できるようにポータブルにしました。

このループの3番目のスクリプトタスクは、ワークシート名を使用して、次のループで使用される変数を設定して宛先テーブルを決定します。

第3ループ(第2内側ループ)は、ワークシートのデータを含むオブジェクト変数の行を列挙します。これには、上記のワークシート名で設定された変数値に基づいて、2つのソース列から正しい宛先表にデータをインポートする1​​つのSQLの実行タスクが含まれています。ワークシート名が常に一貫しているわけではないので、このループはオブジェクト変数に直接接続するため、ソース列を名前で呼び出す必要はありません。むしろ、それぞれをForeachループ内の宛先変数に割り当て、そのデータを行単位でテーブルに渡すだけです。

皆様のご協力とご援助をいただき、ありがとうございます。

+0

このhttp://www.excel-sql-server.com/excel-import-to-sql-server-using-distributed-queries.htm(アドホック、ダイナミックパラメータ、プロセスでの許可など)を有効にしましたか? )、SSMSでそのファイルからデータを取得しますか? – gofr1

答えて

0

通常、すぐにそのメッセージが表示されたときは、ExcelFileの変数名のスペルが間違っていることを意味します。私はまた、SQLクエリを実行すると、このエラーが発生し、nullを返します。ライドラインが実行されるまで、あなたのコードのセクションをコメントアウトすることをお勧めします。

なぜExcel接続がうまくいかないのか分かりません。ファイルがUNCパス上にあり、問題を引き起こしている場合は、スクリプトタスクを使用して、動作する場所にファイルを移動できます。

+0

変数Joe Cの良いキャッチ!このタスクではExcelFilesを使用していますが、前のタスクで使用した「ExcelFile」という名前の変数があります。実際のパッケージで使用するものではありません。 –

0

Joe Cが正しいかもしれません。間違った名前で変数を参照している可能性があります。変数/パラメータをスクリプトタスクに渡しましたか?

まだ、データフロースクリプトタスクの1つを使用していない理由はわかりません。あなたは、入力 - と出力列を定義し、スクリプトタスクのコードでそれらを埋めることができます。

public override void CreateNewOutputRows() 
    { 
     /* 
      Add rows by calling the AddRow method on the member variable named "<Output Name>Buffer". 
      For example, call MyOutputBuffer.AddRow() if your output was named "MyOutput". 
     */ 
    } 

これらの出力行は、あなたのデータフロー内の次のタスクに転送することができます - SSISがそれを好きなだけのよう。また、変数を使用する方がはるかに簡単です。 this.Variables.ExcelFile(f。e。)によって通常のプロパティのようにアクセスできます。

別のノート:(コントロールフローの)スクリプトタスクの結果を設定することを忘れないでください。タスクは正常に終了する可能性がありますが、次のシーケンスフローには制約はありません。あなたの仕事は、そのような他の人が複雑になるとき

Dts.TaskResult = (int)ScriptResults.Success;

+0

データフロースクリプトタスクにはExcel接続が必要だと思いますが、正しいですか?受信したファイル名は標準のUNC名前付けに従わないため、Excel接続が失敗するか、少なくとも真であると思われます。スペースや特殊文字を削除して手動でファイルの名前を変更すると、Excel接続が機能します。クライアントがテンプレートを使用できるようにするには、離れていなければなりません。 –

+0

場所は常に同じですか?なぜファイルの名前を変更するためにスクリプトタスクを使用しないのですか?次に、別の(スクリプト)タスクが続くExcel-Sourceを取得します。 – Johannes

+0

それは可能な解決策かもしれませんヨハネス、私はまた、名前にスペースを持つようにワークシートの名前を変更する必要があります。私はそのオプションを調べ、私が遭遇する問題を見ていきます。ありがとう! –

0

うわー、あなたはそれを嫌いではありません!だからあなたの問題を解決する方法はたくさんありますが、私の個人的な意見はすべてスクリプトタスクの中にあるので、ロジックを完了して完了するのが簡単になりますが、@Johannesもまた別の良い方法をもたらします。スクリプトの作業には2つの場所があり、コーディングと思考プロセスのかなり異なる方法です。 1つは、制御フローで使用できる「スクリプトタスク」です。これは、コーディングする場所のように見え、オブジェクトを変数に追加します。 enter image description here
2つ目は、データフロータスクで使用できる「スクリプトコンポーネント」です。 enter image description here前者は、他のすべてとは無関係に動作するスタンドアロンスクリプトと考える必要があり、後者はデータフロータスクに組み込まれ、ソース、デスティネーション、またはトランスフォーメーションとして機能します。これは、消費されるレコードセット変数(オブジェクト)を設定することに役立つことを意味します。

オプション1では、現在のところあなたがコードを完成させるために必要なのは、C#を追加してあなたの欲望のSQLテーブルを更新することです。ここで私は私がこれを行うに私のパッケージのいずれかから盗んだいくつかのコードされています。私はこれについて何かを持っているために使用されるオプション2について

  SqlConnection sqlConnection = new SqlConnection(sqlConnectionString); 
     sqlConnection.Open(); 

     SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConnection); 
     bulkCopy.DestinationTableName = _stagingTableName; 
     foreach (DataColumn col in _jobRecDT.Columns) 
     { 
      //System.Windows.Forms.MessageBox.Show(col.ColumnName); 
      bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName); 
     } 


     bulkCopy.WriteToServer(_jobRecDT); 

     sqlConnection.Close(); 

は、おそらく@Johannesは転がっリンクを持っているか、誰かがそれをここにコメントすることができます。しかし、この方法では多くのコードを再利用できるはずですが、それを「スクリプトコンポーネント」に移動する必要があります。次に、オブジェクトのレコードセットスキーマを定義し、データフロータスクの他のソースと同様に使用します。

考慮すべき2つの問題があり、さらなるロジックが必要になります。 1)オプション1を使用する場合は、バルク・コピーを使用する前に期待どおりにテーブル/データセットの名前を変更するか、カラム・マッピングを動的に管理する必要があります。 2)データフローオプションでは、最終的なレコードセット変数を移入する前にデータセットを変換して、常に同じ列とデータ型を持つようにする必要があります。

両方のオプションのパフォーマンスとデータの有効性に関する考慮事項があります。最初の方がパフォーマンスが向上する可能性がありますが、SSISではデータの有効/エラーチェックが処理されません。オプション2では、大きなデータセットのSSISエラーチェックとパフォーマンスの利点が得られます。データセットが非常に大きい場合、両方のオプションを調整する必要があります。スレッディングなどの追加の考慮事項がありますが、私はあなたに当てはまるとは思わない。

こちらがお役に立てば幸いです。

+0

詳細な例についてはMattさんに感謝します! –

+0

私の喜びSSISのスクリプトを理解しなければならなかったのは初めてでした。 – Matt

0

解決策の概要を元に私の元の質問を編集してくれました。誰かが質問をしたり、詳細/例を希望される場合は、私に知らせてください。

関連する問題