Python

SSIS_Util: SSIS package file(.dtsx)에서 SQL을 추출하는 도구

ProDA 2021. 12. 5.

이 글은 새로운 블로그로 옮겼습니다. 5초후 자동으로 이동합니다.

▶ 새로운 블로그 주소: https://prodskill.com/

▶ 새로운 글 주소: https://prodskill.com/extract-sql-from-ssis-package-file/

최근 SSIS(SQL Server Integration Services)를 사용하는 업무 중에 SQL 분석을 위해 간단히 만들어서 사용한 도구를 소개한다.

 

목차


    1. SSIS와 SSIS_Util 개요

    1.1. SSIS 개요

    SSIS는 "SQL Server Integration Services"의 약자이고, Microsoft 문서에 다음과 같이 정의되어 있다.

    출처: SQL Server Integration Services - SQL Server Integration Services (SSIS) | Microsoft Docs

    SSIS(SQL Server Integration Services) 개요
    SSIS(SQL Server Integration Services) 개요

    SQL Server Integration Services는 엔터프라이즈 수준 데이터 통합 및 데이터 변환 솔루션을 빌드하는 데 필요한 플랫폼입니다. Integration Services를 사용하여 파일을 복사 또는 다운로드하고, 데이터 웨어하우스를 로드하고, 데이터를 정리 및 마이닝하고, SQL Server 개체와 데이터를 관리하여 복잡한 비즈니스 문제를 해결합니다.

    Integration Services 는 XML 데이터 파일, 플랫 파일, 관계형 데이터 원본과 같은 다양한 원본에서 데이터를 추출 및 변환한 다음 하나 이상의 대상으로 로드할 수 있습니다.

    Integration Services에는 풍부한 기본 제공 작업 및 변환 집합, 패키지 빌드용 그래픽 도구 및 패키지를 저장, 실행 및 관리하는 Integration Services 카탈로그 데이터베이스가 포함됩니다.

    그래픽 Integration Services 도구를 사용하여 코드를 한 줄도 작성하지 않고 솔루션을 만들 수 있습니다. 광범위한 Integration Services 개체 모델을 프로그래밍하여 패키지를 프로그래밍 방식으로 만들고 사용자 지정 태스크 및 기타 패키지 개체를 코딩할 수도 있습니다.

     

    간단하게 요약하면 SSIS는 다양한 원본(source)으로부터 데이터를 추출(Extraction), 변환(Transformation)하고 대상(target)에 적재(Load)하는 ETL 도구이다.

     

    1.2. SSIS_Util 개요

    SSIS_Util은 SSIS package file(.dtsx)에서 SQL을 추출하는 도구이다. SQL 외에 작업 흐름 정보, DB ConnectionString도 함께 추출한다.

    SSIS package file(.dtsx)은 Visual Studio에서 열수 있다. 시각적으로 데이터 처리 절차를 확인할 수 있는 장점은 있지만, SQL 전체를 검색하거나 DB 연결 정보 전체를 확인하기가 번거롭다.

    SSIS_Util로 필요한 정보를 별도의 파일로 추출하면 분석의 효율성을 높일 수 있겠다 싶어 만들었다.

     

    소스코드는 github에 업로드해 두었다.

    https://github.com/DAToolset/SSIS_Util

     

    GitHub - DAToolset/SSIS_Util: extract connection, SQL from SSIS(SQL Server Integration Service ) package file(.dtsx)

    extract connection, SQL from SSIS(SQL Server Integration Service ) package file(.dtsx) - GitHub - DAToolset/SSIS_Util: extract connection, SQL from SSIS(SQL Server Integration Service ) package fil...

    github.com

     

     

    2. SSIS package file(.dtsx) 구조

    dtsx 파일 내용은 XML 구조로 정의되어 있다. 공개되어 있는 dtsx 파일 중 다음 파일의 내용을 일부 발췌해 둔다.

    https://github.com/LearningTechStuff/SSIS-Tutorial/blob/master/Lesson%201.dtsx

    <?xml version="1.0"?>
    <DTS:Executable
      DTS:refId="Package" xmlns:DTS="www.microsoft.com/SqlServer/Dts"
      DTS:ExecutableType="SSIS.Package.3"
      DTS:CreatorName="REDWOOD\Barry"
      DTS:CreatorComputerName="REDWOOD"
      DTS:CreationDate="6/9/2012 4:03:57 PM"
      DTS:PackageType="5"
      DTS:VersionBuild="3"
      DTS:VersionGUID="{3A510DCF-C9DB-4039-9817-E408E5D2BACF}"
      DTS:LastModifiedProductVersion="11.0.2100.60"
      DTS:LocaleID="1033"
      DTS:ObjectName="Lesson 1"
      DTS:DTSID="{7691D130-E411-4B14-9676-A5DB6B8B4ADE}"
      DTS:CreationName="SSIS.Package.3">
      <DTS:Property
        DTS:Name="PackageFormatVersion">6</DTS:Property>
      <DTS:ConnectionManagers>
        <DTS:ConnectionManager
          DTS:refId="Package.ConnectionManagers[localhost.AdventureWorksDW2012]"
          DTS:ObjectName="localhost.AdventureWorksDW2012"
          DTS:DTSID="{F094926F-2384-48F7-9515-DB8ACC225303}"
          DTS:CreationName="OLEDB">
          <DTS:ObjectData>
            <DTS:ConnectionManager
              DTS:ConnectionString="Data Source=localhost;Initial Catalog=AdventureWorksDW2012;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;" />
          </DTS:ObjectData>
        </DTS:ConnectionManager>
        <DTS:ConnectionManager
          DTS:refId="Package.ConnectionManagers[Sample Flat File Source Data]"
          DTS:ObjectName="Sample Flat File Source Data"
          DTS:DTSID="{6DF5747F-57C2-4173-89B6-1A14A807DA3E}"
          DTS:CreationName="FLATFILE">
          <DTS:ObjectData>
            <DTS:ConnectionManager
              DTS:Format="Delimited"
              DTS:LocaleID="1033"
              DTS:HeaderRowDelimiter="_x000D__x000A_"
              DTS:RowDelimiter=""
              DTS:TextQualifier="_x003C_none_x003E_"
              DTS:CodePage="1252"
              DTS:ConnectionString="C:\Program Files\Microsoft SQL Server\110\Samples\Integration Services\Tutorial\Creating a Simple ETL Package\Sample Data\SampleCurrencyData.txt">
              <DTS:FlatFileColumns>
                <DTS:FlatFileColumn
                  DTS:ColumnType="Delimited"
                  DTS:ColumnDelimiter="_x0009_"
                  DTS:DataType="4"
                  DTS:TextQualified="True"
                  DTS:ObjectName="AverageRate"
                  DTS:DTSID="{786CE995-4B7C-41CE-BE81-37933271E018}"
                  DTS:CreationName="" />
                <DTS:FlatFileColumn
                  DTS:ColumnType="Delimited"
                  DTS:ColumnDelimiter="_x0009_"
                  DTS:MaximumWidth="3"
                  DTS:DataType="130"
                  DTS:TextQualified="True"
                  DTS:ObjectName="CurrencyID"
                  DTS:DTSID="{7E5616A8-02FD-49A7-ADEC-AB276D909B75}"
                  DTS:CreationName="" />
                <DTS:FlatFileColumn
                  DTS:ColumnType="Delimited"
                  DTS:ColumnDelimiter="_x0009_"
                  DTS:DataType="133"
                  DTS:TextQualified="True"
                  DTS:ObjectName="CurrencyDate"
                  DTS:DTSID="{AF15821D-08F4-4FAA-B167-262FE448C298}"
                  DTS:CreationName="" />
                <DTS:FlatFileColumn
                  DTS:ColumnType="Delimited"
                  DTS:ColumnDelimiter="_x000D__x000A_"
                  DTS:DataType="4"
                  DTS:TextQualified="True"
                  DTS:ObjectName="EndOfDayRate"
                  DTS:DTSID="{B9FC32CE-DE09-4626-B539-7E50A8469675}"
                  DTS:CreationName="" />
              </DTS:FlatFileColumns>
            </DTS:ConnectionManager>
          </DTS:ObjectData>
        </DTS:ConnectionManager>
      </DTS:ConnectionManagers>
      <DTS:Variables />
      <DTS:Executables>
        <DTS:Executable
          DTS:refId="Package\Extract Sample Currency Data"
          DTS:ExecutableType="SSIS.Pipeline.3"
          DTS:TaskContact="Performs high-performance data extraction, transformation and loading;Microsoft Corporation; Microsoft SQL Server; (C) 2007 Microsoft Corporation; All Rights Reserved;http://www.microsoft.com/sql/support/default.asp;1"
          DTS:LocaleID="-1"
          DTS:ObjectName="Extract Sample Currency Data"
          DTS:DTSID="{E967823D-AA98-44CA-8B5D-E29585E4C0F0}"
          DTS:Description="Data Flow Task"
          DTS:CreationName="SSIS.Pipeline.3">
          <DTS:Variables />
          <DTS:ObjectData>
            <pipeline
              version="1">
              <components>
                <component
                  refId="Package\Extract Sample Currency Data\Extract Sample Currency Data"
                  name="Extract Sample Currency Data"
                  componentClassID="{D23FD76B-F51D-420F-BBCB-19CBF6AC1AB4}"
                  description="Flat File Source"
                  localeId="1033"
                  usesDispositions="true"
                  version="1"
                  contactInfo="Flat File Source;Microsoft Corporation; Microsoft SQL Server; (C) Microsoft Corporation; All Rights Reserved; http://www.microsoft.com/sql/support;1">
                  <properties>
                    <property
                      name="RetainNulls"
                      dataType="System.Boolean"
                      description="Specifies whether zero-length columns are treated as null.">false</property>
                    <property
                      name="FileNameColumnName"
                      dataType="System.String"
                      description="Specifies the name of an output column containing the file name. If no name is specified, no output column containing the file name will be generated."></property>
                  </properties>
                  <connections>
    ...

     

     

    dtsx 파일의 상세 구조는 아래 페이지에서 확인할 수 있다.

    https://docs.microsoft.com/en-us/openspecs/sql_data_portability/ms-dtsx/235600e9-0c13-4b5b-a388-aa3c65aec1dd

     

    [MS-DTSX]: Data Transformation Services Package XML File Format

    Specifies the Data Transformation Services Package File Format (DTSX), which is an XML-based file format that stores the instructions for the

    docs.microsoft.com

     

     

    3. SSIS_Util Python Code

    소스코드는 아래 경로에서 확인할 수 있다.

    https://github.com/DAToolset/SSIS_Util/blob/main/ssis_util.py

    이 코드를 실행하려면 XML parser로 lxml package 설치가 필요하다.

    pip install lxml

     

    전체 소스코드를 아래에 붙여둔다.

    from lxml import etree
    import os
    import datetime
    import csv
    import argparse
    
    # region XML Tag definition
    pfx = '{www.microsoft.com/'
    tag_Executable = pfx + 'SqlServer/Dts}Executable'
    tag_ObjectName = pfx + 'SqlServer/Dts}ObjectName'
    atr_refId = pfx + 'SqlServer/Dts}refId'
    tag_Objectdata = pfx + 'SqlServer/Dts}ObjectData'
    tag_SqlTaskData = pfx + 'sqlserver/dts/tasks/sqltask}SqlTaskData'
    atr_ConnectionID = pfx + 'sqlserver/dts/tasks/sqltask}Connection'
    atr_SqlStatementSource = pfx + 'sqlserver/dts/tasks/sqltask}SqlStatementSource'
    atr_Disabled = pfx + 'SqlServer/Dts}Disabled'
    
    # precedence
    tag_PrecedenceConstraint = pfx + 'SqlServer/Dts}PrecedenceConstraint'
    tag_From = pfx + 'SqlServer/Dts}From'
    tag_To = pfx + 'SqlServer/Dts}To'
    
    # connection
    tag_ConnectionManager = pfx + 'SqlServer/Dts}ConnectionManager'
    tag_CreationName = pfx + 'SqlServer/Dts}CreationName'
    atr_ConnectionString = pfx + 'SqlServer/Dts}ConnectionString'
    atr_DTSID = pfx + 'SqlServer/Dts}DTSID'
    
    # variable
    tag_Variable = pfx + 'SqlServer/Dts}Variable'
    tag_Expression = pfx + 'SqlServer/Dts}Expression'
    tag_Namespace = pfx + 'SqlServer/Dts}Namespace'
    # endregion
    
    
    def get_sql_using_xpath(file_name) -> (str, dict):
    
        tree = etree.parse(file_name)
        root = tree.getroot()
    
        con_dic = {}  # Key: ConnectionName , Value: ConnectionProperty
        con_id_dic = {}  # Key: ConnectionID , Value: ConnectionName
        con_cnt = 0
        var_dic = {}  # Key: "NameSpace::ObjectName", Value: Expression, etc...
    
        for ele in root.findall(f'.//{tag_ConnectionManager}[@{atr_DTSID}]'):  # extract connection list
            con_cnt += 1
            con_id = ele.attrib[atr_DTSID]
            con_name = ele.attrib[tag_ObjectName]
            cre_name = ele.attrib[tag_CreationName]
            con_str = ""
            con_ele = ele.find(f'.//{tag_Objectdata}/{tag_ConnectionManager}')
            if con_ele is not None and atr_ConnectionString in con_ele.attrib:
                con_str = con_ele.attrib[atr_ConnectionString]
            con_id_dic[con_id] = con_name
            con_dic[con_name] = f'{con_cnt}. {con_name}\n  - CreationName:{cre_name}\n  - ConnectionString:[{con_str}]'
        con_text = '\n\n'.join(f'{v}' for k, v in con_dic.items())
    
        for ele in root.findall(f'.//{tag_Variable}[@{tag_Expression}]'):  # extract variable list
            var_name = ele.attrib[tag_Namespace] + "::" + ele.attrib[tag_ObjectName]  # Key: "NameSpace::ObjectName"
            var_val = ele.attrib[tag_Expression]
            var_dic[var_name] = var_val
    
        sql_str = ""
        for ele in root.findall(f'.//{tag_Executable}/{tag_Objectdata}/{tag_SqlTaskData}/../..'):  # extract SQL list
            if atr_refId not in ele.attrib:
                continue
            ref_id = ele.attrib[atr_refId]
            sql_ele = ele.find(f'.//{tag_Objectdata}/{tag_SqlTaskData}[@{atr_SqlStatementSource}]')
            if is_disabled(ele):  # exclude Disabled task(also ancestor Elements)
                continue
            tsk_sql_str = ""
            tsk_con_name = ""
            tsk_con_str = ""
            if sql_ele is not None:
                tsk_con_id = sql_ele.attrib[atr_ConnectionID]
                tsk_con_name = con_id_dic[tsk_con_id]
                tsk_con_str = con_dic[tsk_con_name]
                tsk_sql_str = sql_ele.attrib[atr_SqlStatementSource]
                sql_str += f'/* [Control Flow(제어 흐름) TaskName: {ref_id}]\n   [Connection: {tsk_con_str}]\n*/\n{tsk_sql_str}'
                sql_str += get_line_separator()
    
        data_flow_str = ""
        for ele in root.findall(f'.//pipeline/components/component[@refId]'):  # extract data flow list
            if is_disabled(ele):  # exclude Disabled task(also ancestor Elements)
                continue
            ref_id = ele.attrib['refId']
            sql_cmd_var_str = ""; sql_cmd_str = ""; open_rowset_str = ""
            sql_cmd_var_ele = ele.find(f'.//property[@name="SqlCommandVariable"]')
            if sql_cmd_var_ele is not None:
                sql_cmd_var_str = sql_cmd_var_ele.text
            sql_cmd_ele = ele.find(f'.//property[@name="SqlCommand"]')
            if sql_cmd_ele is not None:
                sql_cmd_str = sql_cmd_ele.text
            open_rowset_ele = ele.find(f'.//property[@name="OpenRowset"]')
            if open_rowset_ele is not None and open_rowset_ele.text is not None:
                open_rowset_str = "OpenRowset: " + open_rowset_ele.text
            str_list = [sql_cmd_var_str, sql_cmd_str, open_rowset_str]
            try:
                data_flow_val = next(s for s in str_list if s)
            except:
                data_flow_val = ''
    
            con_str = ''
            con_ele = ele.find(f'.//connections/connection[@connectionManagerID]')
            if con_ele is not None:
                con_str = con_ele.attrib['connectionManagerID']
            data_flow_str += f'/* [Data Flow(데이터 흐름) TaskName: {ref_id}]\n   [Connection: {con_str}] */\n{data_flow_val}'
            data_flow_str += get_line_separator()
    
        prd_text = ""
        for ele in root.findall(f'.//{tag_PrecedenceConstraint}[@{tag_From}]'):  # make precedence string
            prd_from = ele.attrib[tag_From]
            prd_to = ele.attrib[tag_To]
            prd_text += f'[{prd_from}] --> [{prd_to}]\n'
    
        result_str = f"""/* Precedence Constraint(실행 순서)
    {prd_text}*/
    {get_line_separator()}
    /* Connections(연결 정보)
    {con_text}
    */
    {get_line_separator()}
    {data_flow_str}
    {get_line_separator()}
    {sql_str}"""
    
        return result_str, con_dic
    
    
    def get_current_datetime() -> str:
        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
    
    
    def get_line_separator() -> str:
        return f'\n\n/*{"*" * 100}*/\n\n'
    
    
    def is_disabled(ele) -> bool:
        """check if the element has 'Disabled' attribute and it's value is 'True', and check parent element"""
        if atr_Disabled in ele.attrib and ele.attrib[atr_Disabled].lower() == "true":
            return True
        else:
            pele = ele.find('..')
            if pele is None:
                return False
            else:
                return is_disabled(pele)
    
    
    def main():
        parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
        parser.add_argument('--in_path', required=True, type=str,
                            help='input path with dtsx files')
        parser.add_argument('--out_path', required=True, type=str,
                            help='output path with extracted sql files from dtsx files')
        args = parser.parse_args()
        in_path = os.path.abspath(args.in_path)
        out_path = os.path.abspath(args.out_path)
        file_list = []
        print(f'[{get_current_datetime()}] Start Get File List...')
        in_abspath = os.path.abspath(in_path)  # os.path.abspath('.') + '\\test_files'
        file_types = ('.dtsx',)
        for root, dir, files in os.walk(in_abspath):
            for file in sorted(files):
                # exclude
                if file.startswith('~'):
                    continue
                # include
                if file.endswith(file_types):
                    file_list.append(root + '\\' + file)
    
        print(f'[{get_current_datetime()}] Finish Get File List. ({len(file_list)} files)')
    
        print(f'[{get_current_datetime()}] Start Extract File Contents...')
        con_list = []
        for dtsx_file in file_list:
            print(dtsx_file)
            sql_file = dtsx_file.replace(in_path, out_path) + ".sql"
            sql_file_dir = os.path.dirname(sql_file)
            os.makedirs(name=sql_file_dir, exist_ok=True)
            result_str, con_dic = get_sql_using_xpath(dtsx_file)
            tmp_list = []
            for k, v in con_dic.items():
                tmp_list.append([dtsx_file, sql_file, k, v])
            if len(tmp_list) > 0:
                con_list.append(tmp_list)
            with open(sql_file, "w", encoding="utf8") as file:
                file.write(result_str)
    
        csv_file = out_path + "\\con_sql.csv"
        with open(csv_file, 'w', newline='', encoding='ansi') as file:
            writer = csv.writer(file)
            writer.writerow(["dtsx_file", "sql_file", "connection name", "connection property"])
            # writer.writerow(con_list)
            for con in con_list:
                for con2 in con:
                    writer.writerow(con2)
    
        print(f'[{get_current_datetime()}] Finish Extract File Contents. ({len(file_list)} files)')
    
    
    if __name__ == '__main__':
        main()
    
    # sample dtsx file: https://github.com/LearningTechStuff/SSIS-Tutorial

     

    • 7행: NameSpace가 있는 XML은 tag와 attribute에 해당 NameSpace를 명시해야 한다. NameSpace를 반복적으로 문자열로 작성하면 코드 가독성이 떨어져서 별도의 변수로 미리 선언해 둔다.
    • 38행: lxml package로 입력 파일을 parsing 한다.
    • 46행: XPath 구문으로 connection 정보를 순회한다.
    • 56행: connection 정보를 별도의 dictionary에 저장한다. key는 connection명, value는 connection 문자열이다. 이 정보는 78행에서 task의 connection명으로 connection 문자열을 얻을 때 사용한다.
    • 70행, 85행: 자기 자신 node를 포함하여 조상(ancestor) node 중 하나라도 disabled라면 추출하지 않도록 한다. 이 코드는 불필요한 정보를 추출하지 않으려고 추가했다. 전체 정보를 추출하려면 이 코드를 주석 처리하면 된다.
    • 112행: 작업 선행/후행 관계를 추출한다.
    • 117~126행: 추출한 전체 정보를 저장하기 위한 구조로 구성한다.
    • 139행: 70행, 85행에서 호출하는 함수이다. 한 단계 위의 조상을 확인하기 위하여 재귀적으로 호출한다.
    • 188행: 117~126행에서 만들어진 추출 결과를 .sql 파일로 저장한다.
    • 192~198행: 56행에서 만들어 둔 connection 정보를 모두 모아서 별도의 csv 파일로 저장한다.

     

    4. SSIS_Util 출력 결과 예시

    4.1. sql 파일 예시

    /* Precedence Constraint(실행 순서)
    [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task1] --> [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task2]
    [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task2] --> [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task3]
    [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task3] --> [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task4]
    [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task4] --> [Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task5]
    */
    
    
    /******************************************************************************************************/
    
    
    /* Connections(연결 정보)
    1. key 캐시연결
      - CreationName:CACHE
      - ConnectionString:[]
    
    2. 10.x.x.1.admin.admin
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.1;User ID=admin;Initial Catalog=admin;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]
    
    3. 10.x.x.1.db3.admin
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.1;User ID=admin;Initial Catalog=db3;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]
    
    4. 10.x.x.1.db3.admin
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.1;Initial Catalog=db4;Provider=SQLNCLI11;Integrated Security=SSPI;]
    
    5. localhost.db2
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.2;User ID=admin;Initial Catalog=db2;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]
    
    6. localhost.db1
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.3;User ID=admin;Initial Catalog=db1;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]
    */
    
    
    /******************************************************************************************************/
    
    /* [Data Flow(데이터 흐름) TaskName: Package\update\task4 Source]
       [Connection: Package.ConnectionManagers[10.x.x.4.db6.admin]] */
    select ...
      from ...
    
    /******************************************************************************************************/
    
    /* [Data Flow(데이터 흐름) TaskName: Package\update\task4 Target]
       [Connection: Package.ConnectionManagers[localhost.db1]] */
    OpenRowset: [dbo].[table1]
    
    /******************************************************************************************************/
    
    
    /* [Control Flow(제어 흐름) TaskName: Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task1]
       [Connection: 6. localhost.db1
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.3;User ID=admin;Initial Catalog=db1;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]]
    */
    delete from ... where dt = @dt
    
    insert ...
    (...)
    select
    ...
    from (
        select 
            ...
        from #tempp a
            inner join ... b
                on ...
        where (...)
    ) t
    where ...
    
    /******************************************************************************************************/
    
    /* [Control Flow(제어 흐름) TaskName: Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task2]
       [Connection: 5. localhost.db2
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.2;User ID=admin;Initial Catalog=db2;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]]
    */
    if object_id('tempdb.dbo.#tempp') is not null
        drop table #tempp
    
    select
         ...
      into #tempp
      from ... a
     where dt = @dt
     group by ...
    
    merge ... a
        using (...) b
            on ...
    when not matched then 
        insert(...)
        values(...);
    
    /******************************************************************************************************/
    
    /* [Control Flow(제어 흐름) TaskName: Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task3]
       [Connection: 3. localhost.db3
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.1;User ID=admin;Initial Catalog=db3;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]]
    */
    
    update b
       set ...
      from ... a
           inner join ... b
            on ...
    where ...
    
    /******************************************************************************************************/
    
    /* [Control Flow(제어 흐름) TaskName: Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task4]
       [Connection: 4. localhost.db4
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.1;Initial Catalog=db4;Provider=SQLNCLI11;Integrated Security=SSPI;]]
    */
    update b
       set ...
      from ... a
           inner join ... b
            on ...
    where ...
    
    /******************************************************************************************************/
    
    /* [Control Flow(제어 흐름) TaskName: Package\Foreach 루프 컨테이너\시퀀스 컨테이너\task5]
       [Connection: 2. localhost.admin
      - CreationName:OLEDB
      - ConnectionString:[Data Source=10.x.x.1;User ID=admin;Initial Catalog=admin;Provider=SQLNCLI11;Persist Security Info=True;Auto Translate=False;]]
    */
    if object_id('tempdb.dbo.#tempm') is not null
        drop table #tempm
    
    select
          ...
      into #tempm
      from a
     where a.dt = @dt
    
    merge ... a
    using (...) b
       on ...
      and ...
    when not matched then
        insert (...)
        values (...);
    
    /******************************************************************************************************/

     

    4.2. .csv 파일 예시

    csv 파일은 dtsx_file, sql_file, connection name, connection property 정보를 포함한다.

    SSIS_Util 출력 csv 파일 예시
    SSIS_Util 출력 csv 파일 예시

     


    SSIS를 사용하는 환경에서 전체 관점의 분석이 필요할 경우 유용하게 사용할 수 있을 것으로 기대한다.

    소스코드의 버그 또는 개선사항이 있으면 댓글로 남겨주기 바란다.

    댓글

    💲 추천 글