Comment écrire la sortie d'une procédure stockée directement dans un fichier sur un FTP sans utiliser de fichiers locaux ou temporaires ?

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

Question

Je souhaite obtenir les résultats d'une procédure stockée et les placer dans un fichier CSV sur un emplacement FTP.

Le problème est que je ne peux pas créer un fichier local/temporaire sur lequel je peux ensuite utiliser un FTP.

L'approche que j'adoptais consistait à utiliser un package SSIS pour créer un fichier temporaire, puis à disposer d'une tâche FTP dans le pack pour transférer le fichier par FTP, mais nos administrateurs de base de données ne permettent pas la création de fichiers temporaires sur des serveurs.

en réponse à Yaakov Ellis

Je pense que nous devrons convaincre les administrateurs de base de données de me laisser utiliser au moins un partage sur un serveur qu'ils n'exploitent pas, ou leur demander comment ils le feraient.

en réponse à Kev

J'aime l'idée de l'intégration CLR, mais je ne pense pas que nos DBA sachent même ce que c'est mdr et ils ne le permettraient probablement pas non plus.Mais je pourrai probablement le faire dans une tâche de script dans un package SSIS qui peut être planifié.

Était-ce utile?

La solution

Cet exemple étape par étape s'adresse à ceux qui pourraient tomber sur cette question.Cet exemple utilise Serveur Windows Server 2008 R2 et SSIS2008 R2.Même si l'exemple utilise SSIS2008 R2, la logique utilisée est applicable à SSIS2005 aussi.Grâce à @Kev pour le FTPWebDemande code.

Créez un package SSIS (Étapes pour créer un package SSIS).J'ai nommé le package au format AAAAMMJJ_hhmm au début suivi de DONC signifie Stack Overflow, suivi du Donc identifiant de la question, et enfin une description.Je ne dis pas que vous devriez nommer votre package ainsi.C'est à moi d'y revenir facilement plus tard.Notez que j'ai également deux sources de données, à savoir Travaux d'aventure et Base de données pratique.j'utiliserai Travaux d'aventure source de données, qui pointe vers Travaux d'aventure base de données téléchargée depuis ce lien.Référez-vous à la capture d'écran #1 au bas de la réponse.

Dans le Travaux d'aventure base de données, créez une procédure stockée nommée dbo.GetCurrency en utilisant le script ci-dessous.

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

Dans la section Gestionnaire de connexions du package, cliquez avec le bouton droit et sélectionnez Nouvelle connexion à partir de la source de données.Sur le Sélectionnez la source de données boîte de dialogue, sélectionnez Travaux d'aventure et cliquez D'ACCORD.Vous devriez maintenant voir la source de données Adventure Works sous la section Gestionnaires de connexions.Référez-vous à la capture d'écran #2, #3 et #4.

Sur le package, créez les variables suivantes.Référez-vous à la capture d'écran #5.

  • Délimiteur de colonnes:Cette variable est de type String.Ceci sera utilisé pour séparer les données des colonnes lors de leur écriture dans le fichier.Dans cet exemple, nous utiliserons la virgule (,) et le code est écrit pour gérer uniquement les caractères affichables.Pour les caractères non affichables comme la tabulation ( ), vous devrez peut-être modifier le code utilisé dans cet exemple en conséquence.

  • Nom de fichier:Cette variable est de type String.Il contiendra le nom du fichier.Dans cet exemple, j'ai nommé le fichier Currencies.csv car je vais exporter la liste des noms de devises.

  • Mot de passe FTP:Cette variable est de type String.Celui-ci contiendra le mot de passe du site Web FTP.Idéalement, le package devrait être crypté pour masquer les informations sensibles.

  • FTPChemin distant:Cette variable est de type String.Celui-ci contiendra le chemin du dossier FTP vers lequel le fichier doit être téléchargé.Par exemple, si l'URI FTP complet est ftp://myFTPSite.com/ssis/samples/uploads, alors le RemotePath serait /ssis/samples/uploads.

  • Nom du serveur FTP:Cette variable est de type String.Celui-ci contiendra l'URI racine du site FTP.Par exemple, si l'URI FTP complet est ftp://myFTPSite.com/ssis/samples/uploads, alors le FTPServerName contiendrait ftp://myFTPSite.com.Vous pouvez combiner FTPRemotePath avec cette variable et avoir une seule variable.Cela dépend de vos préférences.

  • Nom d'utilisateur FTP:Cette variable est de type String.Celui-ci contiendra le nom d'utilisateur qui sera utilisé pour se connecter au site FTP.

  • ListeDeDevises:Cette variable est de type Objet.Celui-ci contiendra le jeu de résultats de la procédure stockée et il sera parcouru en boucle dans la tâche de script.

  • Afficher en-tête:Cette variable est de type booléen.Celui-ci contiendra les valeurs vrai/faux.True indique que la première ligne du fichier contiendra les noms de colonnes et False indique que la première ligne ne contiendra pas les noms de colonnes.

  • SQLGetData:Cette variable est de type String.Celui-ci contiendra l'instruction d'exécution de la procédure stockée.Cet exemple utilise la valeur EXEC dbo.GetCurrency

Sur le colis Flux de contrôle onglet, placez un Exécuter la tâche SQL et nommez-le comme Obtenir des données.Double-cliquez sur la tâche Exécuter SQL pour afficher le Exécuter l'éditeur de tâches SQL.Sur le Général section de la Exécuter l'éditeur de tâches SQL, met le Ensemble de résultats à Full result set, le Connexion à Adventure Works, le SQLSourceType à Variable et le Variable Source à User::SQLGetData.Dans la section Ensemble de résultats, cliquez sur le bouton Ajouter.Définissez le nom du résultat sur 0, cela indique l'index et la variable à User::ListOfCurrencies.La sortie de la procédure stockée sera enregistrée dans cette variable objet.Cliquez sur D'ACCORD.Référez-vous à la capture d'écran #6 et #7.

Sur le colis Flux de contrôle , placez une tâche de script sous la tâche d'exécution SQL et nommez-la comme Enregistrer sur FTP.Double-cliquez sur la tâche de script pour afficher le Éditeur de tâches de script.Dans la section Script, cliquez sur le bouton Edit Script… bouton.Référez-vous à la capture d'écran #8.Cela fera apparaître l'éditeur Visual Studio Tools pour Applications (VSTA).Remplacer le code dans la classe ScriptMain dans l'éditeur avec le code donné ci-dessous.Assurez-vous également d'ajouter les instructions using aux espaces de noms. System.Data.OleDb, System.IO, System.Net, System.Text.Référez-vous à la capture d'écran #9 qui met en évidence les changements de code.Fermez l'éditeur VSTA et cliquez sur OK pour fermer l'éditeur de tâches de script.Le code de script prend la variable objet ListOfCurrencies et la stocke dans un DataTable à l'aide d'OleDbDataAdapter car nous utilisons une connexion OleDb.Le code parcourt ensuite chaque ligne et si la variable ShowHeader est définie sur true, le code inclura les noms de colonnes dans la première ligne écrite dans le fichier.Le résultat est stocké dans une variable stringbuilder.Une fois que la variable du générateur de chaîne est renseignée avec toutes les données, le code crée un objet FTPWebRequest et se connecte à l'URI FTP en combinant les variables FTPServerName, FTPRemotePath et FileName à l'aide des informations d'identification fournies dans les variables FTPUserName et FTPPassword.Ensuite, le contenu complet de la variable du générateur de chaîne est écrit dans le fichier.La méthode WriteRowData est créée pour parcourir les colonnes et fournir les noms de colonnes ou les informations sur les données en fonction des paramètres transmis.

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

Une fois les tâches configurées, le flux de contrôle du package devrait ressembler à celui illustré dans la capture d'écran. #10.

Capture d'écran #11 affiche le résultat de l'instruction d'exécution de procédure stockée EXEC dbo.GetCurrency.

Exécutez le package.Capture d'écran #12 montre une exécution réussie du package.

En utilisant le FireFTP module complémentaire disponible dans FireFox navigateur, je me suis connecté au site Web FTP et j'ai vérifié que le fichier a été téléchargé avec succès sur le site Web FTP.Référez-vous à la capture d'écran #13.

L'examen du contenu en ouvrant le fichier dans Notepad++ montre qu'il correspond à la sortie de la procédure stockée.Référez-vous à la capture d'écran #14.

Ainsi, l'exemple a montré comment écrire les résultats d'une base de données sur un site Web FTP sans avoir à utiliser de fichiers temporaires/locaux.

J'espère que cela aide quelqu'un.

Captures d'écran:

#1:Explorateur de solution

Solution_Explorer

#2:New_Connection_From_Data_Source

New_Connection_From_Data_Source

#3:Sélectionner_Data_Source

Select_Data_Source

#4:Gestionnaires_de connexion

Connection_Managers

#5:Variables

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:Contrôle_Flow_Tab

Control_Flow_Tab

#11:Requête_Résultats

Query_Results

#12:Package_Execution_Successful

Package_Execution_Successful

#13:Fichier_Dans_FTP

File_In_FTP

#14:Fichier_Contenu

File_Contents

Autres conseils

Si vous étiez autorisé à implémenter des assemblys d’intégration CLR, vous pourriez réellement utiliser FTP sans avoir à écrire un fichier temporaire :

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

Existe-t-il un serveur que vous pouvez utiliser pour créer un fichier temporaire ?Si tel est le cas, créez un service Web qui renvoie un tableau contenant le contenu du fichier.Appelez le service Web depuis l'ordinateur sur lequel vous pouvez créer un fichier temporaire, utilisez le contenu du tableau pour créer le fichier temporaire et envoyez-le par FTP.

S'il n'y a nulle part du tout où vous pouvez créer un fichier temporaire, je ne vois pas comment vous pourrez envoyer quoi que ce soit par FTP.

Essayez d'utiliser une procédure stockée CLR.Vous pourrez peut-être trouver quelque chose, mais sans créer au préalable un fichier temporaire, cela peut encore être difficile.Pourriez-vous configurer un partage sur une autre machine et y écrire, puis FTP à partir de là ?

Script depuis le serveur FTP et appelez simplement le processus stocké.

Le Catch est que je ne peux pas créer un fichier local / temporaire que je peux alors faire ftp.

Cette restriction n'a aucun sens, essayez de parler gentiment avec DBA et de lui expliquer.Il est tout à fait raisonnable que tout processus ou tâche Windows crée un ou plusieurs fichiers temporaires à l'emplacement approprié, c'est-à-direDossier %TEMP%.En fait, le runtime SSIS lui-même y crée souvent des fichiers temporaires - donc si DBA vous permet d'exécuter SSIS, il est vous permettant de créer des fichiers temporaires :).

Tant que le DBA comprend que ces fichiers temporaires ne lui créent pas de problème ou de charge de travail supplémentaire (expliquez-lui qu'il le fait). pas devez définir des autorisations spéciales, ou les sauvegarder, etc.), il doit accepter de vous laisser les créer.

La seule tâche de maintenance pour DBA consiste à nettoyer périodiquement le répertoire %TEMP% au cas où votre travail SSIS échouerait et laisserait le fichier derrière lui.Mais il devrait quand même le faire, car de nombreux autres processus peuvent faire de même.Un simple travail d'agent SQL fera cela.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top