Come scrivere l'output della procedura memorizzata direttamente su un file su un FTP senza utilizzare file locali o temporanei?

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

Domanda

Desidero ottenere i risultati di una procedura memorizzata e inserirli in un file CSV in una posizione FTP.

Il problema però è che non posso creare un file locale/temporaneo su cui posso poi eseguire l'FTP.

L'approccio che stavo adottando era quello di utilizzare un pacchetto SSIS per creare un file temporaneo e quindi avere un'attività FTP all'interno del pacchetto per trasferire il file tramite FTP, ma i nostri DBA non consentono la creazione di file temporanei su alcun server.

in risposta a Yaakov Ellis

Penso che dovremo convincere i DBA a permettermi di utilizzare almeno una condivisione su un server che non gestiscono, o chiedere loro come farebbero.

in risposta a Kev

Mi piace l'idea dell'integrazione CLR, ma non credo che i nostri DBA sappiano nemmeno di cosa si tratta lol e probabilmente non lo permetterebbero neanche loro.Ma probabilmente sarò in grado di farlo all'interno di un'attività di script in un pacchetto SSIS che può essere pianificato.

È stato utile?

Soluzione

Questo esempio passo passo è per altri che potrebbero imbattersi in questa domanda.Questo esempio utilizza Server Windows Server 2008 R2 E SSIS2008R2.Anche se l'esempio utilizza SSIS2008R2, la logica utilizzata è applicabile a SSIS2005 anche.Grazie a @Kev per il FTPWebRequest codice.

Creare un pacchetto SSIS (Passaggi per creare un pacchetto SSIS).Ho nominato il pacchetto nel formato AAAAMMGG_hhmm all'inizio seguito da COSÌ sta per Stack Overflow, seguito da Quindi ID domanda, e infine una descrizione.Non sto dicendo che dovresti nominare il tuo pacchetto in questo modo.Questo mi servirà per poterlo riferire facilmente in seguito.Nota che ho anche due origini dati, vale a dire Opere d'avventura E Pratica DB.userò Opere d'avventura origine dati, che punta a AdventureWorks database scaricato da questo link.Fare riferimento allo screenshot #1 in fondo alla risposta.

Nel AdventureWorks database, creare una procedura memorizzata denominata dbo.GetCurrency utilizzando lo script indicato di seguito.

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

Nella sezione Connection Manager del pacchetto, fare clic con il pulsante destro del mouse e selezionare Nuova connessione dall'origine dati.Sul Seleziona Origine dati finestra di dialogo, seleziona Opere d'avventura e fare clic OK.Ora dovresti vedere l'origine dati Adventure Works nella sezione Gestioni connessioni.Fare riferimento allo screenshot #2, #3 E #4.

Sul pacchetto creare le seguenti variabili.Fare riferimento allo screenshot #5.

  • Delimitatore di colonna:Questa variabile è di tipo String.Questo verrà utilizzato per separare i dati della colonna quando vengono scritti nel file.In questo esempio utilizzeremo la virgola (,) e il codice verrà scritto per gestire solo i caratteri visualizzabili.Per i caratteri non visualizzabili come tab ( ), potrebbe essere necessario modificare di conseguenza il codice utilizzato in questo esempio.

  • Nome del file:Questa variabile è di tipo String.Conterrà il nome del file.In questo esempio, ho denominato il file Currencies.csv perché esporterò l'elenco dei nomi delle valute.

  • Password FTP:Questa variabile è di tipo String.Questo conterrà la password per il sito Web FTP.Idealmente, il pacchetto dovrebbe essere crittografato per nascondere informazioni sensibili.

  • FTPRemotePath:Questa variabile è di tipo String.Questo conterrà il percorso della cartella FTP in cui caricare il file.Ad esempio, se l'URI FTP completo è ftp://myFTPSite.com/ssis/samples/uploads, il RemotePath sarà /ssis/samples/uploads.

  • NomeServer FTP:Questa variabile è di tipo String.Questo conterrà l'URI root del sito FTP.Ad esempio, se l'URI FTP completo è ftp://myFTPSite.com/ssis/samples/uploads, quindi FTPServerName conterrebbe ftp://mioSitoFTPSit.com.Puoi combinare FTPRemotePath con questa variabile e avere un'unica variabile.Dipende dalle tue preferenze.

  • Nomeutente FTP:Questa variabile è di tipo String.Questo conterrà il nome utente che verrà utilizzato per connettersi al sito FTP.

  • Elenco delle valute:Questa variabile è di tipo Object.Questo conterrà il set di risultati della procedura memorizzata e verrà ripetuto nell'attività Script.

  • ShowHeader:Questa variabile è di tipo Booleano.Questo conterrà valori vero/falso.True indica che la prima riga nel file conterrà nomi di colonne e False indica che la prima riga non conterrà nomi di colonne.

  • SQLGetData:Questa variabile è di tipo String.Questo conterrà l'istruzione di esecuzione della procedura memorizzata.In questo esempio viene utilizzato il valore EXEC dbo.GetCurrency

Sul pacchetto Flusso di controllo scheda, posizionare un Esegui attività SQL e chiamarlo come Ottieni dati.Fare doppio clic sull'attività Esegui SQL per visualizzare il file Eseguire l'editor delle attività SQL.Sul Generale sezione del Eseguire l'editor delle attività SQL, impostare il Set di risultati A Full result set, IL Connessione A Adventure Works, IL SQLSourceType A Variable e il Variabile di origine A User::SQLGetData.Nella sezione Set di risultati, fare clic sul pulsante Aggiungi.Imposta il nome del risultato su 0, questo indica l'indice e la Variabile a User::ListOfCurrencies.L'output della procedura memorizzata verrà salvato in questa variabile oggetto.Clic OK.Fare riferimento allo screenshot #6 E #7.

Sul pacchetto Flusso di controllo scheda, posizionare un'attività script sotto l'attività Esegui SQL e denominarla come Salva su FTP.Fare doppio clic sull'attività Script per visualizzare il file Editor delle attività di script.Nella sezione Script, fare clic su Edit Script… pulsante.Fare riferimento allo screenshot #8.Verrà visualizzato l'editor di Visual Studio Tools for Applications (VSTA).Sostituisci il codice all'interno della classe ScriptMain nell'editor con il codice indicato di seguito.Inoltre, assicurati di aggiungere le istruzioni using agli spazi dei nomi System.Data.OleDb, System.IO, System.Net, System.Text.Fare riferimento allo screenshot #9 che evidenzia le modifiche al codice.Chiudere l'editor VSTA e fare clic su OK per chiudere l'editor delle attività Script.Il codice script prende la variabile oggetto ListOfCurrencies e la memorizza in un DataTable con l'aiuto di OleDbDataAdapter perché stiamo utilizzando la connessione OleDb.Il codice quindi scorre ogni riga e se la variabile ShowHeader è impostata su true, il codice includerà i nomi delle colonne nella prima riga scritta nel file.Il risultato viene archiviato in una variabile stringbuilder.Dopo che la variabile del generatore di stringhe è stata popolata con tutti i dati, il codice crea un oggetto FTPWebRequest e si connette all'URI FTP combinando le variabili FTPServerName, FTPRemotePath e FileName utilizzando le credenziali fornite nelle variabili FTPUserName e FTPPassword.Quindi il contenuto completo della variabile del generatore di stringhe viene scritto nel file.Viene creato il metodo WriteRowData per scorrere le colonne e fornire i nomi delle colonne o le informazioni sui dati in base ai parametri passati.

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

Una volta configurate le attività, il flusso di controllo del pacchetto dovrebbe apparire come mostrato nello screenshot #10.

Immagine dello schermo #11 mostra l'output dell'istruzione di esecuzione della procedura memorizzata EXEC dbo.GetCurrency.

Esegui il pacchetto.Immagine dello schermo #12 mostra l'esecuzione riuscita del pacchetto.

Usando il FireFTP componente aggiuntivo disponibile in FireFox browser, ho effettuato l'accesso al sito Web FTP e ho verificato che il file fosse stato caricato correttamente sul sito Web FTP.Fare riferimento allo screenshot n.13.

L'esame del contenuto aprendo il file in Notepad++ mostra che corrisponde all'output della procedura memorizzata.Fare riferimento allo screenshot n.14.

Pertanto, l'esempio ha dimostrato come scrivere risultati dal database a un sito Web FTP senza dover utilizzare file temporanei/locali.

Spero che questo aiuti qualcuno.

Schermate:

#1:Esploratore di soluzioni

Solution_Explorer

#2:Nuova_connessione_da_origine_dati

New_Connection_From_Data_Source

#3:Seleziona_Origine_dati

Select_Data_Source

#4:Connection_Manager

Connection_Managers

#5:Variabili

Variables

#6:Esegui_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_Codice

Script_Task_VSTA_Code

#10:Control_Flow_Tab

Control_Flow_Tab

#11:Query_Risultati

Query_Results

#12:Pacchetto_Esecuzione_Riuscita

Package_Execution_Successful

#13:File_In_FTP

File_In_FTP

#14:File_Contenuto

File_Contents

Altri suggerimenti

Se ti fosse consentito implementare gli assembly di integrazione CLR potresti effettivamente utilizzare FTP senza dover scrivere un file temporaneo:

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

Esiste un server da qualche parte che puoi utilizzare dove puoi creare un file temporaneo?In tal caso, crea un servizio Web che restituisca un array contenente il contenuto del file.Chiama il servizio Web dal computer in cui puoi creare un file temporaneo, utilizza il contenuto dell'array per creare il file temporaneo e inviarlo tramite ftp.

Se non c'è dove affatto dove puoi creare un file temporaneo, non vedo come potrai inviare qualcosa tramite FTP.

Prova a utilizzare una procedura memorizzata CLR.Potresti riuscire a inventare qualcosa, ma senza prima creare un file temporaneo, potrebbe essere comunque difficile.Potresti impostare una condivisione su un'altra macchina e scrivere su quella, quindi eseguire l'ftp da lì?

Script dal server FTP e chiama semplicemente il proc memorizzato.

Il problema è che non riesco a creare un file locale/temporaneo su cui posso quindi FTP.

Questa restrizione non ha alcun senso, prova a parlare gentilmente con DBA e spiegarglielo.È del tutto ragionevole che qualsiasi processo o lavoro di Windows crei file temporanei nella posizione appropriata, ad es.Cartella %TEMP%.In realtà, il runtime SSIS stesso spesso crea file temporanei lì, quindi se DBA ti consente di eseguire SSIS, he È permettendoti di creare file temporanei :).

Finché DBA capisce che questi file temporanei non gli creano problemi o carico di lavoro aggiuntivo (spiegare che lo fa non deve impostare permessi speciali, o eseguirne il backup, ecc.), dovrebbe accettare di lasciarti creare.

L'unica attività di manutenzione per DBA è pulire periodicamente la directory %TEMP% nel caso in cui il processo SSIS fallisca e lasci il file.Ma dovrebbe farlo comunque, poiché molti altri processi potrebbero fare lo stesso.Un semplice lavoro di SQL Agent lo farà.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top