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

최근 SSIS(SQL Server Integration Services)를 사용하는 업무 중에 SQL 분석을 위해 간단히 제작한 SSIS package file에서 SQL 추출 하는 도구에 대해 살펴본다. 실행방법과 소스코드, sql 파일, csv 파일 출력결과 예시에 대해 확인할 수 있다.

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

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

3. SSIS_Util Python Code

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

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

3.1. 필요한 Package 설치

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

pip install lxml

3.2. SQL 추출 : SSIS_Util 실행 방법

python ssis_util.py --in_path <dtdx file path> --out_path <output sql file path>

in_path에 지정한 경로의 하위 전체 경로를 재귀 탐색하여 .dtsx 파일을 찾고, 해당 파일에서 SQL 내용을 추출하여 out_path에 저장한다.

3.3. SQL 추출 전체 소스코드

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

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를 사용하는 환경에서 전체 관점의 분석이 필요할 경우 유용하게 사용할 수 있을 것으로 기대한다.

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

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

ko_KR한국어