Как записать выходные данные хранимой процедуры непосредственно в файл на FTP без использования локальных или временных файлов?

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

Вопрос

Я хочу получить результаты хранимой процедуры и поместить их в CSV-файл на FTP-сервере.

Загвоздка, однако, в том, что я не могу создать локальный / временный файл, который я затем могу передать по FTP.

Подход, который я использовал, состоял в том, чтобы использовать пакет SSIS для создания временного файла, а затем создать FTP-задачу внутри пакета для передачи файла по FTP, но наши администраторы баз данных не разрешают создавать временные файлы ни на каких серверах.

в ответ Яакову Эллису

Я думаю, нам нужно будет убедить администратора базы данных разрешить мне использовать хотя бы общий ресурс на сервере, с которым они не работают, или спросить их, как бы они это сделали.

в ответ Кеву

Мне нравится идея интеграции CLR, но я не думаю, что наш администратор базы данных даже знает, что это такое лол и они, вероятно, тоже этого не допустили бы.Но я, вероятно, смогу сделать это в рамках задачи сценария в пакете SSIS, который можно запланировать.

Это было полезно?

Решение

Этот пошаговый пример предназначен для других, кто может наткнуться на этот вопрос.В этом примере используется Сервер Windows Server 2008 R2 и SSIS 2008 R2.Несмотря на то, что в примере используется SSIS 2008 R2, используемая логика применима к SSIS 2005 также хорошо.Благодаря @Kev для Запрос FtpWebRequest код.

Создайте пакет SSIS (Шаги по созданию пакета SSIS).Я назвал пакет в формате YYYYMMDD_hhmm в начале, за которым следует ИТАК расшифровывается как Stack Overflow, за которым следует ИТАК, идентификатор вопроса, и, наконец, описание.Я не говорю, что вы должны называть свой пакет именно так.Это для того, чтобы я мог легко вернуться к этому вопросу позже.Обратите внимание, что у меня также есть два источника данных, а именно Приключенческие Произведения и Практика БД.Я буду использовать Приключенческие Произведения источник данных, который указывает на Приключенческие работы база данных, загруженная с эта ссылка.Смотрите скриншот #1 в нижней части ответа.

В Приключенческие работы базе данных, создайте хранимую процедуру с именем dbo.Получить валюту используя приведенный ниже скрипт.

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.

  • Ограничитель столбцов:Эта переменная имеет тип String.Это будет использоваться для разделения данных столбца при их записи в файл.В этом примере мы будем использовать запятую (,), и код написан для обработки только отображаемых символов.Для не отображаемых символов, таких как tab ( ), вам может потребоваться соответствующим образом изменить код, используемый в этом примере.

  • Имя файла:Эта переменная имеет тип String.Он будет содержать имя файла.В этом примере я назвал файл Currencies.csv, потому что я собираюсь экспортировать список названий валют.

  • Пароль FTPPassword:Эта переменная имеет тип String.Это будет содержать пароль к FTP-сайту.В идеале пакет должен быть зашифрован, чтобы скрыть конфиденциальную информацию.

  • Путь FTPRemotePath:Эта переменная имеет тип String.Это будет содержать путь к папке FTP, в которую должен быть загружен файл.Например, если полный URI FTP равен ftp://myFTPSite.com/ssis/samples/uploads, тогда удаленный путь был бы /ssis/samples/uploads.

  • Имя FTPServerName:Эта переменная имеет тип String.Он будет содержать URI корня FTP-сайта.Например, если полный URI FTP равен ftp://myFTPSite.com/ssis/samples/uploads, тогда FTPServerName содержал бы ftp://myFTPSite.com.Вы можете объединить FTPRemotePath с этой переменной и получить единственную переменную.Это зависит от ваших предпочтений.

  • Имя пользователя FTPUserName:Эта переменная имеет тип String.Оно будет содержать имя пользователя, которое будет использоваться для подключения к веб-сайту FTP.

  • Список валют:Эта переменная имеет тип Object.Это будет содержать результирующий набор из хранимой процедуры, и он будет зациклен в задаче скрипта.

  • Ведущий шоу:Эта переменная имеет тип Boolean.Это будет содержать значения true /false.Значение True указывает, что первая строка в файле будет содержать имена столбцов, а значение False указывает, что первая строка не будет содержать имен столбцов.

  • Функции:Эта переменная имеет тип String.Это будет содержать инструкцию о выполнении хранимой процедуры.В этом примере используется значение EXEC dbo.GetCurrency

На упаковке Поток управления вкладку, разместите Выполнить задачу SQL и назовите это как Получение данных.Дважды щелкните на задаче Execute SQL, чтобы вызвать Выполнить Редактор задач SQL.На Общая информация раздел программы Выполнить Редактор задач SQL, установить Результирующий набор Для Full result set, тот самый Соединение Для Adventure Works, тот самый Тип SQLSourceType Для Variable и тот Переменный источник Для User::SQLGetData.В разделе Результирующий набор нажмите кнопку Добавить.Задайте для имени результата значение 0, это указывает индекс и переменную для User::ListOfCurrencies.Выходные данные хранимой процедуры будут сохранены в этой объектной переменной.Щелчок ОК.Смотрите скриншот #6 и #7.

На упаковке Поток управления вкладку, поместите задачу скрипта под задачей Execute SQL и назовите ее следующим образом Сохранить на FTP.Дважды щелкните по задаче скрипта, чтобы вызвать Редактор задач скрипта.В разделе Сценарий нажмите кнопку Edit Script… кнопка.Смотрите скриншот #8.После этого откроется редактор Visual Studio Tools for Applications (VSTA).Замените код внутри класса ScriptMain в редакторе с кодом, приведенным ниже.Кроме того, убедитесь, что вы добавили инструкции using в пространства имен System.Data.OleDb, System.IO, System.Net, System.Text.Смотрите скриншот #9 это подчеркивает изменения в коде.Закройте редактор VSTA и нажмите "Ок", чтобы закрыть редактор задач скрипта.Код скрипта принимает объектную переменную ListOfCurrencies и сохраняет ее в DataTable с помощью OleDbDataAdapter, потому что мы используем OleDb-соединение.Затем код перебирает каждую строку, и если переменной ShowHeader присвоено значение true, код будет включать имена столбцов в первой строке, записанные в файл.Результат сохраняется в переменной stringbuilder.После заполнения переменной string builder всеми данными код создает объект FtpWebRequest и подключается к Uri FTP, комбинируя переменные FTPServerName, FTPRemotePath и FileName, используя учетные данные, указанные в переменных FTPUserName и FTPPassword.Затем в файл записывается полное содержимое переменной string builder.Метод 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 дополнение доступно в FireFox через браузер я зашел на веб-сайт FTP и убедился, что файл был успешно загружен на веб-сайт FTP.Смотрите скриншот #13.

Проверка содержимого путем открытия файла в Notepad ++ показывает, что оно совпадает с выводом хранимой процедуры.Смотрите скриншот #14.

Таким образом, в примере продемонстрировано, как записать результаты из базы данных на FTP-сайт без необходимости использования временных / локальных файлов.

Надеюсь, это кому-нибудь поможет.

Скриншоты:

#1:Решение_эКсплорер

Solution_Explorer

#2:New_Connection_From_Data_Source Новое подключение из_данных

New_Connection_From_Data_Source

#3:Выбираем_данные_источника

Select_Data_Source

#4:Connection_Managers ( Управляющие подключением )

Connection_Managers

#5:Переменные

Variables

#6:Execute_SQL_Task_Editor_General исполнитель_sql_task_editor_general

Execute_SQL_Task_Editor_General

#7:execute_sql_task_editor_result_set_исполнитель_sql_task_editor_result_set

Execute_SQL_Task_Editor_Result_Set

#8:Script_Task_Editor - Редактор скриптов

Script_Task_Editor

#9:Script_Task_VSTA_Code скрипт_задачи_sta_code

Script_Task_VSTA_Code

#10:Control_Flow_Tab - элемент управления

Control_Flow_Tab

#11:Запросы_результаты

Query_Results

#12:пакет_выполнения_успешный

Package_Execution_Successful

#13:Файл_In_FTP

File_In_FTP

#14:Файл_Контенты

File_Contents

Другие советы

Если бы вам было разрешено реализовывать сборки интеграции 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();
    }
}

Есть ли где-нибудь сервер, который вы могли бы использовать, где вы могли бы создать временный файл?Если это так, создайте веб-сервис, который возвращает массив, содержащий содержимое файла.Вызовите веб-службу с компьютера, на котором вы можете создать временный файл, используйте содержимое массива для создания временного файла и передайте его по ftp.

Если нет места, где вообще там, где вы можете создать временный файл, я не понимаю, как вы сможете отправлять что-либо по FTP.

Попробуйте использовать хранимую процедуру CLR.Возможно, вам удастся что-то придумать, но без предварительного создания временного файла это все равно может оказаться затруднительным.Не могли бы вы настроить общий ресурс на другом компьютере и записывать на него, а затем по ftp оттуда?

Запустите скрипт с FTP-сервера и просто вызовите сохраненную процедуру.

Загвоздка, однако, в том, что я не могу создать локальный / временный файл, который я затем могу передать по FTP.

Это ограничение не имеет никакого смысла, попробуйте вежливо поговорить с администратором базы данных и объяснить это ему / ей.Для любого процесса или задания Windows абсолютно разумно создавать временные файлы в соответствующем месте, т.е.Папка %TEMP%.На самом деле, сама среда выполнения SSIS часто создает там временные файлы, поэтому, если администратор базы данных разрешает вам запускать SSIS, он является позволяет создавать временные файлы :).

До тех пор, пока администратор базы данных понимает, что эти временные файлы не создают для него проблем или дополнительной рабочей нагрузки (объясните, что он это делает нет должны быть установлены специальные разрешения или созданы резервные копии для них и т.д.), он должен согласиться разрешить вам их создавать.

Единственной задачей обслуживания для администратора базы данных является периодическая очистка каталога %TEMP% на случай, если ваше задание SSIS завершится неудачей и файл останется.Но он все равно должен это сделать, так как многие другие процессы могут делать то же самое.Это сделает простое задание агента SQL.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top