Wie schreibe ich die Ausgabe einer gespeicherten Prozedur direkt in eine Datei auf einem FTP, ohne lokale oder temporäre Dateien zu verwenden?

StackOverflow https://stackoverflow.com/questions/20587

Frage

Ich möchte die Ergebnisse einer gespeicherten Prozedur abrufen und sie in einer CSV-Datei an einem FTP-Speicherort ablegen.

Der Haken ist jedoch, dass ich keine lokale/temporäre Datei erstellen kann, die ich dann per FTP übertragen kann.

Der Ansatz, den ich gewählt habe, bestand darin, ein SSIS-Paket zu verwenden, um eine temporäre Datei zu erstellen, und dann einen FTP-Task im Paket zu haben, um die Datei per FTP zu übertragen. Unsere Datenbankadministratoren erlauben jedoch nicht, dass temporäre Dateien auf Servern erstellt werden.

als Antwort an Yaakov Ellis

Ich denke, wir müssen die Datenbankadministratoren davon überzeugen, mir zumindest eine Freigabe auf einem Server zu erlauben, den sie nicht betreiben, oder sie fragen, wie sie das machen würden.

als Antwort an Kev

Mir gefällt die Idee der CLR-Integration, aber ich glaube nicht, dass unsere Datenbankadministratoren überhaupt wissen, was das ist Lol und sie würden es wahrscheinlich auch nicht zulassen.Aber ich werde dies wahrscheinlich innerhalb einer Skriptaufgabe in einem SSIS-Paket tun können, das geplant werden kann.

War es hilfreich?

Lösung

Dieses Schritt-für-Schritt-Beispiel ist für andere gedacht, die möglicherweise auf diese Frage stoßen.Dieses Beispiel verwendet Windows Server 2008 R2-Server Und SSIS 2008 R2.Auch wenn das Beispiel verwendet SSIS 2008 R2, die verwendete Logik ist anwendbar auf SSIS 2005 sowie.Dank an @Kev für die FTPWebRequest Code.

Erstellen Sie ein SSIS-Paket (Schritte zum Erstellen eines SSIS-Pakets).Ich habe das Paket am Anfang im Format JJJJMMTT_hhmm benannt, gefolgt von ALSO steht für Stack Overflow, gefolgt von SO Frage-ID, und schließlich eine Beschreibung.Ich sage nicht, dass Sie Ihr Paket so benennen sollten.Dies dient dazu, dass ich später problemlos darauf zurückgreifen kann.Beachten Sie, dass ich nämlich auch zwei Datenquellen habe Abenteuerwerke Und DB üben.Ich werde es verwenden Abenteuerwerke Datenquelle, die darauf verweist AdventureWorks Datenbank heruntergeladen von dieser Link.Siehe Screenshot #1 am Ende der Antwort.

Im AdventureWorks Erstellen Sie in der Datenbank eine gespeicherte Prozedur mit dem Namen dbo.GetCurrency Verwenden Sie das unten angegebene Skript.

CREATE PROCEDURE [dbo].[GetCurrency]
AS
BEGIN
    SET NOCOUNT ON;
    SELECT 
    TOP 10      CurrencyCode
            ,   Name
            ,   ModifiedDate 
    FROM        Sales.Currency
    ORDER BY    CurrencyCode
END
GO

Klicken Sie mit der rechten Maustaste auf den Abschnitt „Verbindungsmanager“ des Pakets und wählen Sie „Aus“. Neue Verbindung von der Datenquelle.Auf der Wählen Sie Datenquelle aus Dialog, wählen Sie Abenteuerwerke und klicken OK.Sie sollten nun die Adventure Works-Datenquelle im Abschnitt „Verbindungsmanager“ sehen.Siehe Screenshot #2, #3 Und #4.

Erstellen Sie auf dem Paket die folgenden Variablen.Siehe Screenshot #5.

  • ColumnDelimiter:Diese Variable ist vom Typ String.Dies wird verwendet, um die Spaltendaten zu trennen, wenn sie in die Datei geschrieben werden.In diesem Beispiel verwenden wir Komma (,) und der Code ist so geschrieben, dass er nur anzeigbare Zeichen verarbeitet.Für nicht anzeigbare Zeichen wie Tabulatorzeichen ( ) müssen Sie möglicherweise den in diesem Beispiel verwendeten Code entsprechend ändern.

  • Dateiname:Diese Variable ist vom Typ String.Es enthält den Namen der Datei.In diesem Beispiel habe ich die Datei „Currencies.csv“ genannt, da ich eine Liste mit Währungsnamen exportieren möchte.

  • FTPPasswort:Diese Variable ist vom Typ String.Dieses enthält das Passwort für die FTP-Website.Idealerweise sollte das Paket verschlüsselt sein, um vertrauliche Informationen zu verbergen.

  • FTPRemotePath:Diese Variable ist vom Typ String.Dies enthält den FTP-Ordnerpfad, in den die Datei hochgeladen werden soll.Zum Beispiel, wenn der vollständige FTP-URI lautet ftp://myFTPSite.com/ssis/samples/uploads, dann wäre der RemotePath /ssis/samples/uploads.

  • FTPServerName:Diese Variable ist vom Typ String.Dies enthält den Root-URI der FTP-Site.Zum Beispiel, wenn der vollständige FTP-URI lautet ftp://myFTPSite.com/ssis/samples/uploads, dann würde der FTPServerName enthalten ftp://myFTPSite.com.Sie können FTPRemotePath mit dieser Variablen kombinieren und eine einzige Variable haben.Es liegt an Ihren Vorlieben.

  • FTPBenutzername:Diese Variable ist vom Typ String.Dieser enthält den Benutzernamen, der für die Verbindung zur FTP-Website verwendet wird.

  • ListOfCurrencies:Diese Variable ist vom Typ Objekt.Dies enthält den Ergebnissatz der gespeicherten Prozedur und wird in der Skriptaufgabe durchlaufen.

  • Kopfzeile anzeigen:Diese Variable ist vom Typ Boolean.Dies enthält die Werte wahr/falsch.True gibt an, dass die erste Zeile in der Datei Spaltennamen enthält, und False gibt an, dass die erste Zeile keine Spaltennamen enthält.

  • SQLGetData:Diese Variable ist vom Typ String.Dies enthält die Ausführungsanweisung der gespeicherten Prozedur.In diesem Beispiel wird der Wert EXEC dbo.GetCurrency verwendet

Auf dem Paket Kontrollfluss Tab, platzieren Sie ein SQL-Aufgabe ausführen und benennen Sie es als Daten bekommen.Doppelklicken Sie auf den Task „SQL ausführen“, um den zu öffnen Führen Sie den SQL-Task-Editor aus.Auf der Allgemein Abschnitt der Führen Sie den SQL-Task-Editor aus, stellen Sie die ein ResultSet Zu Full result set, Die Verbindung Zu Adventure Works, Die SQLSourceType Zu Variable und das Quellvariable Zu User::SQLGetData.Klicken Sie im Abschnitt „Ergebnismenge“ auf die Schaltfläche „Hinzufügen“.Legen Sie den Ergebnisnamen auf fest 0, dies gibt den Index und die Variable an User::ListOfCurrencies.Die Ausgabe der gespeicherten Prozedur wird in dieser Objektvariablen gespeichert.Klicken OK.Siehe Screenshot #6 Und #7.

Auf dem Paket Kontrollfluss Platzieren Sie auf der Registerkarte „SQL ausführen“ eine Skriptaufgabe unter der Aufgabe „SQL ausführen“ und benennen Sie sie als Auf FTP speichern.Doppelklicken Sie auf die Skriptaufgabe, um die aufzurufen Skriptaufgaben-Editor.Klicken Sie im Abschnitt „Skript“ auf Edit Script… Taste.Siehe Screenshot #8.Dadurch wird der Visual Studio Tools for Applications (VSTA)-Editor geöffnet.Ersetzen Sie den Code innerhalb der Klasse ScriptMain im Editor mit dem unten angegebenen Code.Stellen Sie außerdem sicher, dass Sie die using-Anweisungen zu den Namespaces hinzufügen System.Data.OleDb, System.IO, System.Net, System.Text.Siehe Screenshot #9 das die Codeänderungen hervorhebt.Schließen Sie den VSTA-Editor und klicken Sie auf „OK“, um den Skriptaufgaben-Editor zu schließen.Der Skriptcode verwendet die Objektvariable ListOfCurrencies und speichert sie mithilfe von OleDbDataAdapter in einer DataTable, da wir eine OleDb-Verbindung verwenden.Der Code durchläuft dann jede Zeile und wenn die Variable ShowHeader auf „true“ gesetzt ist, enthält der Code die Spaltennamen in der ersten Zeile, die in die Datei geschrieben wird.Das Ergebnis wird in einer Stringbuilder-Variablen gespeichert.Nachdem die String-Builder-Variable mit allen Daten gefüllt wurde, erstellt der Code ein FTPWebRequest-Objekt und stellt eine Verbindung zum FTP-URI her, indem er die Variablen FTPServerName, FTPRemotePath und FileName unter Verwendung der in den Variablen FTPUserName und FTPPassword bereitgestellten Anmeldeinformationen kombiniert.Anschließend wird der vollständige Inhalt der String-Builder-Variablen in die Datei geschrieben.Die Methode WriteRowData wird erstellt, um Spalten zu durchlaufen und die Spaltennamen oder Dateninformationen basierend auf den übergebenen Parametern bereitzustellen.

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);
        }
    }
}

Sobald die Aufgaben konfiguriert wurden, sollte der Kontrollfluss des Pakets wie im Screenshot gezeigt aussehen #10.

Bildschirmfoto #11 zeigt die Ausgabe der Ausführungsanweisung der gespeicherten Prozedur EXEC dbo.GetCurrency.

Führen Sie das Paket aus.Bildschirmfoto #12 zeigt die erfolgreiche Ausführung des Pakets an.

Verwendung der FireFTP Add-on verfügbar in Feuerfuchs Browser habe ich mich bei der FTP-Website angemeldet und überprüft, ob die Datei erfolgreich auf die FTP-Website hochgeladen wurde.Siehe Screenshot #13.

Das Untersuchen des Inhalts durch Öffnen der Datei in Notepad++ zeigt, dass er mit der Ausgabe der gespeicherten Prozedur übereinstimmt.Siehe Screenshot #14.

Das Beispiel zeigte also, wie man Ergebnisse aus der Datenbank auf eine FTP-Website schreibt, ohne temporäre/lokale Dateien verwenden zu müssen.

Hoffe das hilft jemandem.

Screenshots:

#1:Lösungsforscher

Solution_Explorer

#2:Neue_Verbindung_aus_Datenquelle

New_Connection_From_Data_Source

#3:Select_Data_Source

Select_Data_Source

#4:Verbindungsmanager

Connection_Managers

#5:Variablen

Variables

#6:Execute_SQL_Task_Editor_General

Execute_SQL_Task_Editor_General

#7:Execute_SQL_Task_Editor_Result_Set

Execute_SQL_Task_Editor_Result_Set

#8:Script_Task_Editor

Script_Task_Editor

#9:Script_Task_VSTA_Code

Script_Task_VSTA_Code

#10:Control_Flow_Tab

Control_Flow_Tab

#11:Abfrage_Ergebnisse

Query_Results

#12:Package_Execution_Successful

Package_Execution_Successful

#13:Datei_In_FTP

File_In_FTP

#14:Dateiinhalte

File_Contents

Andere Tipps

Wenn Sie CLR-Integrationsassemblys implementieren dürften, könnten Sie FTP tatsächlich verwenden, ohne eine temporäre Datei schreiben zu müssen:

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();
    }
}

Gibt es irgendwo einen Server, auf dem Sie eine temporäre Datei erstellen können?Wenn ja, erstellen Sie einen Webdienst, der ein Array zurückgibt, das den Inhalt der Datei enthält.Rufen Sie den Webdienst von dem Computer aus auf, auf dem Sie eine temporäre Datei erstellen können, verwenden Sie den Inhalt des Arrays, um die temporäre Datei zu erstellen und sie per FTP zu übertragen.

Wenn es kein Wo gibt überhaupt wo Sie eine temporäre Datei erstellen können, sehe ich nicht, wie Sie irgendetwas per FTP senden können.

Versuchen Sie es mit einer gespeicherten CLR-Prozedur.Möglicherweise fällt Ihnen etwas ein, aber ohne zunächst eine temporäre Datei zu erstellen, könnte es immer noch schwierig sein.Könnten Sie eine Freigabe auf einem anderen Computer einrichten und darauf schreiben und dann von dort aus eine FTP-Verbindung herstellen?

Skript vom FTP-Server und rufen Sie einfach die gespeicherte Prozedur auf.

Der Haken ist jedoch, dass ich keine lokale/temporäre Datei erstellen kann, über die ich dann über FTP kann.

Diese Einschränkung macht keinen Sinn. Versuchen Sie, freundlich mit dem DBA zu sprechen und es ihm/ihr zu erklären.Es ist für jeden Windows-Prozess oder Job völlig sinnvoll, temporäre Dateien an einem geeigneten Ort zu erstellen, d. h.%TEMP%-Ordner.Tatsächlich erstellt die SSIS-Laufzeit selbst dort häufig temporäre Dateien. Wenn DBA Ihnen also die Ausführung von SSIS ermöglicht, ist dies der Fall Ist Damit können Sie temporäre Dateien erstellen :).

Solange der DBA versteht, dass diese temporären Dateien für ihn kein Problem oder keine zusätzliche Arbeitsbelastung darstellen (erklären Sie, dass dies der Fall ist). nicht (Sie müssen spezielle Berechtigungen festlegen oder sie sichern usw.), sollte er zustimmen, dass Sie sie erstellen dürfen.

Die einzige Wartungsaufgabe für DBA besteht darin, das Verzeichnis %TEMP% regelmäßig zu bereinigen, falls Ihr SSIS-Job fehlschlägt und die Datei zurückbleibt.Aber er sollte dies trotzdem tun, da viele andere Prozesse möglicherweise dasselbe tun.Dies wird durch einen einfachen SQL-Agent-Job erledigt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top