로컬 또는 임시 파일을 사용하지 않고 FTP의 파일에 직접 저장 프로시저 출력을 쓰는 방법은 무엇입니까?

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

문제

저장 프로시저의 결과를 가져와 FTP 위치의 CSV 파일에 저장하고 싶습니다.

하지만 문제는 FTP로 전송할 수 있는 로컬/임시 파일을 만들 수 없다는 것입니다.

내가 취한 접근 방식은 SSIS 패키지를 사용하여 임시 파일을 만든 다음 팩 내에 FTP 작업을 포함하여 파일을 FTP로 전송하는 것이었지만 DBA는 어떤 서버에서도 임시 파일을 만드는 것을 허용하지 않습니다.

Yaakov Ellis에게 답장을 보내다

제 생각에는 DBA가 운영하지 않는 서버에서 최소한 공유를 사용할 수 있도록 설득하거나 어떻게 할 것인지 물어봐야 할 것 같습니다.

케브에 대한 답장으로

CLR 통합 아이디어는 마음에 들지만 DBA는 그것이 무엇인지조차 모르는 것 같습니다. ㅋㅋㅋ 그리고 그들도 아마 그것을 허용하지 않을 것이다.하지만 예약 가능한 SSIS 패키지의 스크립트 작업 내에서 이 작업을 수행할 수 있을 것입니다.

도움이 되었습니까?

해결책

이 단계별 예는 이 질문을 우연히 발견할 수 있는 다른 사람들을 위한 것입니다.이 예제에서는 윈도우 서버 2008 R2 서버 그리고 SSIS 2008 R2.그럼에도 불구하고 예제에서는 SSIS 2008 R2, 사용된 논리는 다음에 적용 가능합니다. SSIS 2005 또한.덕분에 @Kev 에 대한 FTP웹요청 암호.

SSIS 패키지 생성(SSIS 패키지를 만드는 단계).처음에 YYYYMMDD_hhmm 형식으로 패키지 이름을 지정했습니다. 그래서 스택 오버플로(Stack Overflow)를 의미하며 그 뒤에는 그래서 질문 ID, 마지막으로 설명입니다.나는 패키지 이름을 이렇게 지정해야 한다고 말하는 것이 아닙니다.나중에 다시 쉽게 참조할 수 있도록 하기 위한 것입니다.또한 두 개의 데이터 소스가 있습니다. 어드벤처 웍스 그리고 실습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.

  • 열구분자:이 변수는 문자열 유형입니다.이는 파일에 기록될 때 열 데이터를 분리하는 데 사용됩니다.이 예에서는 쉼표(,)를 사용하며 표시 가능한 문자만 처리하도록 코드가 작성되었습니다.탭( )과 같이 표시할 수 없는 문자의 경우 이에 따라 이 예제에 사용된 코드를 변경해야 할 수도 있습니다.

  • 파일 이름:이 변수는 문자열 유형입니다.여기에는 파일 이름이 포함됩니다.이 예에서는 통화 이름 목록을 내보낼 것이므로 파일 이름을 Currities.csv로 지정했습니다.

  • FTP비밀번호:이 변수는 문자열 유형입니다.여기에는 FTP 웹사이트의 비밀번호가 포함됩니다.이상적으로는 중요한 정보를 숨기기 위해 패키지를 암호화해야 합니다.

  • 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를 이 변수와 결합하여 단일 변수를 가질 수 있습니다.그것은 당신의 취향에 달려 있습니다.

  • FTP사용자 이름:이 변수는 문자열 유형입니다.여기에는 FTP 웹사이트에 연결하는 데 사용되는 사용자 이름이 포함됩니다.

  • 통화 목록:이 변수는 Object 유형입니다.여기에는 저장 프로시저의 결과 집합이 포함되며 스크립트 작업에서 반복됩니다.

  • 헤더 표시:이 변수는 부울 유형입니다.여기에는 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.그러면 VSTA(Visual Studio Tools for Application) 편집기가 나타납니다.클래스 내에서 코드 교체 ScriptMain 아래 주어진 코드를 사용하여 편집기에서.또한 네임스페이스에 using 문을 추가해야 합니다. System.Data.OleDb, System.IO, System.Net, System.Text.스크린샷 참조 #9 코드 변경 사항을 강조 표시합니다.VSTA 편집기를 닫고 확인을 클릭하여 스크립트 작업 편집기를 닫습니다.스크립트 코드는 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 웹사이트에 로그인하여 파일이 FTP 웹사이트에 성공적으로 업로드되었는지 확인했습니다.스크린샷 # 참조13.

Notepad++에서 파일을 열어 내용을 검사하면 저장 프로시저 출력과 일치하는 것으로 나타납니다.스크린샷 # 참조14.

따라서 이 예에서는 임시/로컬 파일을 사용하지 않고도 데이터베이스의 결과를 FTP 웹 사이트에 쓰는 방법을 보여주었습니다.

누군가에게 도움이 되기를 바랍니다.

스크린샷:

#1:솔루션_탐색기

Solution_Explorer

#2:New_Connection_From_Data_Source

New_Connection_From_Data_Source

#3:Select_Data_Source

Select_Data_Source

#4:연결_관리자

Connection_Managers

#5:변수

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

#9:Script_Task_VSTA_Code

Script_Task_VSTA_Code

#10:Control_Flow_Tab

Control_Flow_Tab

#11:쿼리_결과

Query_Results

#12:패키지_실행_성공

Package_Execution_Successful

#13:파일_인_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 서버에서 스크립트를 작성하고 저장된 proc을 호출하기만 하면 됩니다.

그래도 캐치는 내가 FTP를 넘을 수있는 로컬/임시 파일을 만들 수 없다는 것입니다.

이 제한은 의미가 없습니다. DBA와 친절하게 대화하고 설명하십시오.모든 Windows 프로세스 또는 작업이 적절한 위치에 임시 파일을 생성하는 것은 전적으로 합리적입니다.%TEMP% 폴더.실제로 SSIS 런타임 자체는 임시 파일을 생성하는 경우가 많습니다. 따라서 DBA가 SSIS 실행을 허용하면 ~이다 임시 파일을 만들 수 있습니다 :).

DBA가 이러한 임시 파일로 인해 문제나 추가 작업 부하가 발생하지 않는다는 점을 이해하는 한(그는 그렇게 한다고 설명합니다.) ~ 아니다 특별한 권한을 설정하거나 백업해야 하는 등), 그는 귀하가 해당 권한을 생성하는 데 동의해야 합니다.

DBA의 유일한 유지 관리 작업은 SSIS 작업이 실패하고 파일이 남아 있는 경우 %TEMP% 디렉터리를 주기적으로 정리하는 것입니다.그러나 다른 많은 프로세스도 동일한 작업을 수행할 수 있으므로 어쨌든 이 작업을 수행해야 합니다.간단한 SQL 에이전트 작업으로 이 작업을 수행합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top