ローカル ファイルや一時ファイルを使用せずに、ストアド プロシージャの出力を FTP 上のファイルに直接書き込むにはどうすればよいですか?
-
09-06-2019 - |
質問
ストアド プロシージャの結果を取得し、それを CSV ファイルとして FTP の場所に配置したいと考えています。
ただし、問題は、FTP で転送できるローカル/一時ファイルを作成できないことです。
私が採用していたアプローチは、SSIS パッケージを使用して一時ファイルを作成し、そのパック内でファイルを FTP 送信する FTP タスクを使用するというものでしたが、DBA はどのサーバー上でも一時ファイルを作成することを許可していませんでした。
ヤアコフ・エリスへの返信
DBA を説得して、DBA が運用していないサーバー上の共有を少なくとも使用できるようにするか、どのようにするかを尋ねる必要があると思います。
ケブへの返信
CLR 統合のアイデアは気に入っていますが、DBA はそれが何なのかさえ知らないと思います 笑 そしておそらく彼らもそれを許さないだろう。ただし、スケジュール可能な SSIS パッケージのスクリプト タスク内でこれを実行できる可能性があります。
解決
この段階的な例は、この質問に遭遇する可能性のある他の人のためのものです。この例では、 Windows Server 2008 R2 サーバー そして SSIS 2008 R2. 。ただし、例では次のように使用しています SSIS 2008 R2, 、使用されるロジックは以下に適用できます。 SSIS 2005 同じように。おかげで @Kev
のために FTPWebリクエスト コード。
SSIS パッケージを作成します (SSIS パッケージを作成する手順)。パッケージに最初に YYYYMMDD_hhmm という形式で名前を付け、その後に続けます。 それで スタック オーバーフローの略で、その後に SO質問ID, 、最後に説明です。パッケージにこのような名前を付けるべきだと言っているのではありません。これは、後で簡単に参照できるようにするためです。2 つのデータ ソースもあることに注意してください。 アドベンチャーワークス そして 実践DB. 。使用します アドベンチャーワークス データ ソース。 アドベンチャーワークス からダウンロードされたデータベース このリンク. 。スクリーンショットを参照 #1 答えの一番下にあります。
の中に アドベンチャーワークス データベースで、という名前のストアド プロシージャを作成します。 dbo.GetCurrency 以下のスクリプトを使用します。
CREATE PROCEDURE [dbo].[GetCurrency]
AS
BEGIN
SET NOCOUNT ON;
SELECT
TOP 10 CurrencyCode
, Name
, ModifiedDate
FROM Sales.Currency
ORDER BY CurrencyCode
END
GO
パッケージの「接続マネージャー」セクションで右クリックし、「接続マネージャー」を選択します。 データソースからの新しい接続. 。で データソースの選択 ダイアログ、選択 アドベンチャーワークス そしてクリックしてください わかりました. 。[接続マネージャー] セクションに Adventure Works データ ソースが表示されます。スクリーンショットを参照 #2, #3 そして #4.
パッケージ上で、次の変数を作成します。スクリーンショットを参照 #5.
列区切り文字:この変数は文字列型です。これは、ファイルに書き込まれるときに列データを分離するために使用されます。この例では、カンマ (,) を使用し、表示可能な文字のみを処理するようにコードを作成します。タブ ( ) などの表示できない文字については、この例で使用されているコードをそれに応じて変更する必要がある場合があります。
ファイル名:この変数は文字列型です。ファイルの名前が含まれます。この例では、通貨名のリストをエクスポートするため、ファイルに Currency.csv という名前を付けています。
FTPパスワード:この変数は文字列型です。これには、FTP Web サイトへのパスワードが含まれます。理想的には、機密情報を隠すためにパッケージを暗号化する必要があります。
FTPリモートパス:この変数は文字列型です。これには、ファイルのアップロード先となる FTP フォルダーのパスが含まれます。たとえば、完全な FTP URI が次の場合、 ftp://myFTPSite.com/ssis/samples/uploads, の場合、RemotePath は /ssis/samples/uploads になります。
FTPサーバー名:この変数は文字列型です。これには、FTP サイトのルート URI が含まれます。たとえば、完全な FTP URI が次の場合、 ftp://myFTPSite.com/ssis/samples/uploads, の場合、FTPServerName には次の内容が含まれます。 ftp://myFTPSite.com. 。FTPRemotePath とこの変数を組み合わせて、単一の変数を持つことができます。それはあなたの好み次第です。
FTPUユーザー名:この変数は文字列型です。これには、FTP Web サイトへの接続に使用されるユーザー名が含まれます。
通貨リスト:この変数はオブジェクト型です。これにはストアド プロシージャからの結果セットが含まれ、スクリプト タスク内でループスルーされます。
ヘッダーの表示:この変数はブール型です。これには true/false の値が含まれます。True はファイルの最初の行に列名が含まれることを示し、False は最初の行に列名が含まれないことを示します。
SQLGetData:この変数は文字列型です。これにはストアド プロシージャの実行ステートメントが含まれます。この例では、値 EXEC dbo.GetCurrency を使用します。
パッケージについて 制御フロー タブに、 SQLタスクの実行 そしてそれを次のように名付けます データの取得. 。「SQL 実行タスク」をダブルクリックして、 SQLタスクエディタの実行. 。で 一般的な のセクション SQLタスクエディタの実行, 、 をセットする 結果セット に Full result set
, 、 繋がり に Adventure Works
, 、 SQLソースタイプ に Variable
そしてその ソース変数 に User::SQLGetData
. 。「結果セット」セクションで、「追加」ボタンをクリックします。結果名を次のように設定します。 0
, 、これはインデックスと変数を示します。 User::ListOfCurrencies
. 。ストアド プロシージャの出力は、このオブジェクト変数に保存されます。クリック わかりました. 。スクリーンショットを参照 #6 そして #7.
パッケージについて 制御フロー タブで、SQL 実行タスクの下にスクリプト タスクを配置し、次の名前を付けます。 FTPに保存. 。スクリプト タスクをダブルクリックして、 スクリプトタスクエディター. 。「スクリプト」セクションで、 Edit Script…
ボタン。スクリーンショットを参照 #8. 。これにより、Visual Studio Tools for Applications (VSTA) エディターが表示されます。クラス内のコードを置き換える ScriptMain
エディターで以下のコードを使用します。また、 using ステートメントを名前空間に必ず追加してください。 System.Data.OleDb
, System.IO
, System.Net
, System.Text
. 。スクリーンショットを参照 #9 コードの変更を強調表示します。VSTA エディタを閉じ、[OK] をクリックしてスクリプト タスク エディタを閉じます。OleDb 接続を使用しているため、スクリプト コードはオブジェクト変数 ListOfCurrency を取得し、OleDbDataAdapter を使用してそれを DataTable に保存します。次に、コードは各行をループし、変数 ShowHeader が true に設定されている場合、コードはファイルに書き込まれる最初の行に列名を含めます。結果は stringbuilder 変数に保存されます。文字列ビルダー変数にすべてのデータが設定された後、コードは FTPWebRequest オブジェクトを作成し、変数 FTPUserName と FTPPassword で提供される資格情報を使用して変数 FTPServerName、FTPRemotePath、FileName を組み合わせて FTP Uri に接続します。次に、完全な文字列ビルダー変数の内容がファイルに書き込まれます。WriteRowData メソッドは、列をループし、渡されたパラメーターに基づいて列名またはデータ情報を提供するために作成されます。
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Data.OleDb;
using System.IO;
using System.Net;
using System.Text;
namespace ST_7033c2fc30234dae8086558a88a897dd.csproj
{
[System.AddIn.AddIn("ScriptMain", Version = "1.0", Publisher = "", Description = "")]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
#region VSTA generated code
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
public void Main()
{
Variables varCollection = null;
Dts.VariableDispenser.LockForRead("User::ColumnDelimiter");
Dts.VariableDispenser.LockForRead("User::FileName");
Dts.VariableDispenser.LockForRead("User::FTPPassword");
Dts.VariableDispenser.LockForRead("User::FTPRemotePath");
Dts.VariableDispenser.LockForRead("User::FTPServerName");
Dts.VariableDispenser.LockForRead("User::FTPUserName");
Dts.VariableDispenser.LockForRead("User::ListOfCurrencies");
Dts.VariableDispenser.LockForRead("User::ShowHeader");
Dts.VariableDispenser.GetVariables(ref varCollection);
OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
DataTable currencies = new DataTable();
dataAdapter.Fill(currencies, varCollection["User::ListOfCurrencies"].Value);
bool showHeader = Convert.ToBoolean(varCollection["User::ShowHeader"].Value);
int rowCounter = 0;
string columnDelimiter = varCollection["User::ColumnDelimiter"].Value.ToString();
StringBuilder sb = new StringBuilder();
foreach (DataRow row in currencies.Rows)
{
rowCounter++;
if (rowCounter == 1 && showHeader)
{
WriteRowData(currencies, row, columnDelimiter, true, ref sb);
}
WriteRowData(currencies, row, columnDelimiter, false, ref sb);
}
string ftpUri = string.Concat(varCollection["User::FTPServerName"].Value,
varCollection["User::FTPRemotePath"].Value,
varCollection["User::FileName"].Value);
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpUri);
ftp.Method = WebRequestMethods.Ftp.UploadFile;
string ftpUserName = varCollection["User::FTPUserName"].Value.ToString();
string ftpPassword = varCollection["User::FTPPassword"].Value.ToString();
ftp.Credentials = new System.Net.NetworkCredential(ftpUserName, ftpPassword);
using (StreamWriter sw = new StreamWriter(ftp.GetRequestStream()))
{
sw.WriteLine(sb.ToString());
sw.Flush();
}
Dts.TaskResult = (int)ScriptResults.Success;
}
public void WriteRowData(DataTable currencies, DataRow row, string columnDelimiter, bool isHeader, ref StringBuilder sb)
{
int counter = 0;
foreach (DataColumn column in currencies.Columns)
{
counter++;
if (isHeader)
{
sb.Append(column.ColumnName);
}
else
{
sb.Append(row[column].ToString());
}
if (counter != currencies.Columns.Count)
{
sb.Append(columnDelimiter);
}
}
sb.Append(System.Environment.NewLine);
}
}
}
タスクを構成すると、パッケージの制御フローはスクリーンショットのようになります。 #10.
スクリーンショット #11 に、ストアド プロシージャ実行ステートメント EXEC dbo.GetCurrency の出力を示します。
パッケージを実行します。スクリーンショット #12 パッケージが正常に実行されたことを示します。
の使用 FireFTP で利用可能なアドオン ファイアフォックス ブラウザで FTP Web サイトにログインし、ファイルが FTP Web サイトに正常にアップロードされたことを確認しました。スクリーンショット#を参照してください13.
Notepad++ でファイルを開いて内容を調べると、ストアド プロシージャの出力と一致することがわかります。スクリーンショット#を参照してください14.
したがって、この例では、一時/ローカル ファイルを使用せずにデータベースから FTP Web サイトに結果を書き込む方法を示しました。
それが誰かの役に立てば幸いです。
スクリーンショット:
#1:ソリューションエクスプローラー
#2:データソースからの新しい接続
#3:データソースの選択
#4:接続マネージャー
#5:変数
#6:実行_SQL_タスク_エディタ_全般
#7:Execute_SQL_Task_Editor_Result_Set
#8:スクリプト_タスク_エディター
#9:スクリプト_タスク_VSTA_コード
#10:コントロール_フロー_タブ
#11:クエリ結果
#12:パッケージ_実行_成功
#13:FTP 内のファイル
#14:ファイルの内容
他のヒント
CLR 統合アセンブリの実装が許可されている場合は、一時ファイルを作成せずに実際に FTP を使用できます。
public static void DoQueryAndUploadFile(string uri, string username, string password, string filename)
{
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(uri + "/" + filename);
ftp.Method = WebRequestMethods.Ftp.UploadFile;
ftp.Credentials = new System.Net.NetworkCredential(username, password);
using(StreamWriter sw = new StreamWriter(ftp.GetRequestStream()))
{
// Do the query here then write to the ftp stream by iterating DataReader or other resultset, following code is just to demo concept:
for (int i = 0; i < 100; i++)
{
sw.WriteLine("{0},row-{1},data-{2}", i, i, i);
}
sw.Flush();
}
}
一時ファイルを作成できるサーバーはどこかにありますか?その場合は、ファイルの内容を含む配列を返す Web サービスを作成します。一時ファイルを作成できるコンピューターから Web サービスを呼び出し、配列の内容を使用して一時ファイルを構築し、それを FTP で転送します。
どこにもないなら まったく 一時ファイルを作成できる場所があるのに、どうやって FTP で何かを送信できるのかわかりません。
CLR ストアド プロシージャを使用してみてください。何か思いつくかもしれませんが、一時ファイルを作成しないとまだ難しいかもしれません。別のマシンに共有を設定してそこに書き込み、そこから FTP してもらえますか?
FTP サーバーからスクリプトを作成し、ストアド プロシージャを呼び出すだけです。
しかし、問題は、私が作成できないことです ローカル/一時ファイルでFTPで転送できます。
この制限には意味がありません。DBA とよく話し合い、説明してください。Windows プロセスまたはジョブにとって、適切な場所に一時ファイルを作成することはまったく合理的です。%TEMP% フォルダー。実際には、SSIS ランタイム自体がそこに一時ファイルを作成することが多いため、DBA が SSIS の実行を許可する場合、DBA は は 一時ファイルを作成できるようになります:)。
DBA が、これらの一時ファイルによって問題や追加の作業負荷が発生しないことを理解している限り (実際に問題が発生したり、追加の作業負荷が発生したりすることを説明してください) ない 特別な権限を設定する必要がある場合、またはそれらをバックアップする必要がある場合など)、彼はあなたがそれらを作成できるようにすることに同意する必要があります。
DBA の唯一のメンテナンス タスクは、SSIS ジョブが失敗してファイルが残された場合に備えて、%TEMP% ディレクトリを定期的にクリーンアップすることです。しかし、他の多くのプロセスも同じことを行う可能性があるため、とにかくこれを行う必要があります。これは、単純な SQL エージェント ジョブで実行できます。