728x90

Microsoft SQL Server 2005의 원시 XML 웹 서비스 개요


Brad Sarsfield, Srik Raghavan _ Microsoft Corporation


적용 대상:
Microsoft SQL Server 2005 (이전에는 "Yukon"으로 알려짐)
Transact-SQL(T-SQL) 언어

요약: SQL Server 2005(이전에는 "Yukon"으로 알려짐) 내의 SOAP/HTTP를 사용하여 XML 웹 서비스를 설정하고 사용하는 방법의 개요를 설명합니다. 여기에는 실례가 되는 예제가 포함되어 있습니다. 이 문서를 최대한 활용하려면 HTTP, SOAP 및 WSDL을 포함한 웹 서비스 기술에 대한 기본적인 이해가 필요합니다.


소개


Microsoft SQL Server 2005는 HTTP를 통해 SOAP를 사용하는 데이터베이스 엔진에 액세스하기 위한 표준 메커니즘을 제공합니다. 이 메커니즘을 이용하면 SOAP/HTTP 요청을 SQL Server에 전송하여 다음을 실행할 수 있습니다.

  • 매개 변수가 있거나 없는 Transact-SQL 일괄 명령문
  • 저장 프로시저, 확장 저장 프로시저, 스칼라 반환 사용자 정의 함수

    SQL Server 20005 이전에는 SQL Server 연결에 사용할 수 있는 메커니즘은 테이블 형식 데이터 스트림(TDS)이라는 사용자 지정 바이너리 프로토콜을 통하는 방법뿐이었습니다. Microsoft는 SOAP/HTTP 액세스를 이용하여, SQL Server에 연결하기 위한 대안으로 사용할 수 있는 문서화된 개방형 프로토콜을 제공했습니다. SOAP/HTTP 액세스를 제공하면, SQL Server에 연결을 시도하는 클라이언트 장치에 Microsoft Data Access Components(MDAC) 스택을 더 이상 설치할 필요가 없기 때문에, 별도의 공간을 필요로 하지 않는 “제로 풋프린트” 클라이언트를 포함하여 보다 광범위한 클라이언트가 SQL Server에 액세스할 수 있습니다. 이로 인해 다양한 플랫폼에서 .NET, SOAP Toolkit, Perl 등과의 상호 운용성이 용이해집니다. SOAP/HTTP 액세스 메커니즘은 XML 및 HTTP와 같이 잘 알려진 기술을 기반으로 하므로 이 메커니즘은 유형이 다른 환경에서 SQL Server에 대한 액세스 및 상호 운용성을 본질적으로 촉진합니다. XML을 구문 분석하고 HTTP 요청을 제출할 수 있는 모든 장치가 이제 SQL Server에 액세스할 수 있습니다.

    많은 기업들이 UNIX 및 Linux 플랫폼에서 실행되는 응용 프로그램에 SQL Server에 대한 연결이 필요할 수 있는 유형이 다른 환경을 가지고 있습니다. 지금까지는 이러한 사용자들이 사용할 수 있는 솔루션은 JDBC 또는 ODBC 드라이버 중 하나를 사용하는 것이었습니다. SOAP/HTTP 액세스는 이제 비용이 낮은 또 하나의 대안을 제공합니다. SOAP/HTTP 액세스는 DBA가 UNIX에서 실행되고 SQL Server 리소스를 관리하는 Perl로 작성된 스크립트를 가지고 있는 경우의 시나리오에 아주 유용합니다. 또한 Microsoft Visual Studio .NET 또는 Jbuilder와 같은 기본 제공 SOAP/HTTP 지원이 포함된 스마트 통합 개발 환경(IDE)을 사용하여 SQL Server에 연결하는 클라이언트 응용 프로그램 개발에도 유용합니다. 이러한 IDE는 SQL Server와의 통신을 추상화하고 클라이언트 응용 프로그램이 사용할 수 있는 개체를 제공하는 프록시 코드를 생성합니다. 또한 SOAP/HTTP를 사용하면 언제 어디서나 SQL Server에 대한 액세스가 가능하므로, 모바일 또는 산발적으로 연결된 장치를 위한 응용 프로그램의 개발이 쉬워집니다. 일단 연결이 되고 서버가 요청 처리를 시작하면 sqlclient, ODBC, OLEDB와 같은 TDS 기반 클라이언트가 사용하는 기존 메커니즘을 사용하여 연결이 모니터링될 수 있습니다.

    요구 사항


    SQL Server 2005의 기본 웹 서비스는 운영 체제로서 Microsoft Windows Server 2003이 필요한데, 그 이유는 웹 서비스가 이 버전이 제공하는 커널 모드 http 드라이버 http.sys에 의존하기 때문입니다. SQL Server는 커널 모드 http.sys 드라이버를 활용하므로 SQL Server로부터 웹 서비스를 노출하기 위해 IIS를 반드시 설치해야 할 필요가 없어 관리가 간소화됩니다. 대신 IIS 설치 여부의 결정은 응용 프로그램 요구 사항에 입각해야 합니다. 예를 들어 특정 응용 프로그램은 명시적인 중간 계층이 있으면 유리합니다. 이와 같은 경우에는 IIS가 유용할 수 있습니다.


    HTTP 종점


    SQL Server를 HTTP SOAP 요청을 기본적으로 수신할 수 있는 웹 서비스로 설정하려면 HTTP 종점을 만들고 종점이 노출하는 메서드를 정의해야 합니다. HTTP 종점을 만들 때에는 들어오는 HTTP 요청을 수신하기 위해 사용하는 고유 URL을 사용하여 만들어야 합니다. 예를 들어, URL "http://servername/sql"을 사용하여 종점을 만드는 경우 http://servername/sql에 전송되는 SOAP 요청은 http.sys에 의해 포착됩니다. 그런 다음 http.sys가 SOAP 요청을 URL과 연결된 종점을 호스팅하는 SQL Server 인스턴스로 라우팅합니다. 거기에서 요청은 SQL Server 내 SOAP 처리 레이어로 전달됩니다.

    SQL Server 인스턴스에는 여러 개의 종점이 있을 수 있는데, 이 종점은 각각 임의 개수의 저장 프로시저(Transact-SQL 또는 CLR을 사용하여 구현됨)를 WebMethod로 종점에서 노출할 수 있고 SOAP 원격 프로시저 호출(RPC)을 통해 호출될 수 있습니다. WebMethod는 노출되는 실제 저장 프로시저와는 다른 이름을 사용할 수 있습니다. WebMethod 이름은 작업 이름으로 WSDL에서 사용자에게 보여지는 것입니다.

    참고 종점의 WebMethod 절은 SQL Server 2005에만 해당되고 ASMX WebMethod 특성과 무관하다는 사실에 주의해야 합니다.

    사용자는 종점에 대해 ad-hoc Transact-SQL 문을 실행할 수 있습니다. 이 작업은 데이터 정의 언어(DDL)에서 선택적 절을 사용하여 종점에서 Batches를 활성화하여 수행됩니다. Batches를 암시적으로 활성화하면 "sqlbatch"라는 WebMethod가 사용자에게 노출됩니다. 이 개념은 다음에 이어지는 섹션에서 더 자세히 설명됩니다.


    HTTP 종점 만들기


    HTTP 종점은 Transact-SQL DDL을 사용하여 만들어지고 관리됩니다. HTTP 종점을 만드는 작업은 SQL Server 2005에 대한 HTTP/SOAP 액세스 활성화의 첫 번째 단계입니다. 각 종점은 이름 및 결합될 경우 종점의 동작(behavior)을 정의하는 옵션의 모음을 가집니다.

    CREATE HTTP ENDPOINT가 사용되는 방법을 예시하기 위해 SQL Server 웹 서비스를 통해 저장 프로시저를 호출하는 Hello World 예제를 살펴보도록 하겠습니다.

    먼저, 다음 T-SQL을 사용하여 마스터 데이터베이스에 hello world라는 저장 프로시저를 만듭니다. 이 저장 프로시저는 단순히 입력 매개 변수에 제공된 문자열을 표시합니다.


    CREATE PROCEDURE hello_world
    (@msg nvarchar(256))
    AS BEGIN
    select @msg as 'message'
    END


    다음 단계로, 다음 T-SQL을 사용하여 WebMethod로서 이 저장 프로시저에 액세스할 수 있도록 하는 HTTP 종점을 만듭니다.


    CREATE ENDPOINT hello_world_endpoint
    STATE = STARTED
    AS HTTP (
    AUTHENTICATION = ( INTEGRATED ),
    PATH = '/sql/demo',
    PORTS = ( CLEAR )
    )
    FOR SOAP (
    WEBMETHOD
    'http://tempuri.org/'.'hello_world'
    (NAME = 'master.dbo.hello_world'),
    BATCHES = ENABLED,
    WSDL = DEFAULT
    )


    모든 종점은 메타데이터 뷰 master.sys.http_endpoints에서 마스터에 저장됩니다. SOAP 메서드를 정의하지 않는 한 종점은 어떤 SOAP 메서드도 가지지 않습니다. 위 예제에서 저장 프로시저 master.dbo.hello_world를 WebMethod 'hello_world'로 노출했습니다. 이와 같이 WebMethod는 임의의 이름을 가질 수 있습니다. 예를 들면, WebMethod가 'http://tempuri.org' 네임스페이스 아래에서 'testproc1'로 호출될 수도 있었습니다. DEFAULT를 WSDL 절의 값으로 지정하면 기본 형식을 사용하는 WSDL를 생성하여 종점이 WSDL 요청에 응답할 수 있습니다. 위 명령문에서 WSDL=NONE을 설정하여 WSDL 생성을 억제할 수 있습니다. 다음에 이어지는 섹션에서 WSDL 생성에 대해 자세히 설명합니다.


    인증 및 보안

    HTTP 종점은 기본, 다이제스트, 통합(NTLM, Kerberos) 및 SQL Auth라는 표준 인증 메커니즘을 지원합니다. 먼저 HTTP 전송 수준에서 인증합니다. 성공적으로 인증되면, 사용자의 SID를 사용하여 SQL을 인증합니다. 이 과정은 LOGIN_TYPE = MIXED를 지정하여 SQL-AUTH가 종점에서 활성화되는 경우를 제외하고 모든 옵션에 적용됩니다. WsSecurity Username 토큰 헤더를 사용하여SQL Auth 자격 증명이 SOAP 패킷의 일부로 전송됩니다. 또한 관리자는 종점을 기준으로 IP 기반 제한을 설정하여, HTTP 종점으로의 액세스를 특정 IP 또는 IP 범위에 대해서만 허용함으로써 종점에 대한 액세스를 제한할 수 있습니다. 개념적으로 “종점”은 “응용 프로그램”입니다. 단일 응용 프로그램을 구현하는 모든 메서드가 종점에 매핑되므로 응용 프로그램에 대한 액세스를 제어하기 위해 종점에 보안이 적용됩니다. 종점은 설계에 의해 보안됩니다. 종점의 보안을 유지할 수 있도록 도와주는 몇 가지 항목이 아래에 나와 있습니다.

  • 기본적으로 Off로 설정되어 있습니다. 매핑된 기본 종점 또는 웹 메서드가 없으므로 이를 명시적으로 생성 및 지정해야 합니다.
  • 또한 개체에도 보안 검사가 적용되므로, 매핑된 저장 프로시저는 사용자가 종점에 대한 연결 권한 및 저장 프로시저에 대한 실행 권한을 가지고 있는 경우에만 실행 가능합니다.
  • 종점에 연결을 위한 익명 지원을 하지 않습니다. WSDL 요청을 포함한 모든 요청이 인증을 받아야 합니다. 클라이언트가 요청을 제출하기 위해서는 SQL Server 원칙에 비추어 인증을 받아야 합니다.

    종점이 만들어지면 sysadmin 역할의 구성원과 종점의 소유자만 종점에 연결할 수 있습니다. 사용자가 종점에 액세스할 수 있도록 연결 권한을 부여해야 합니다. 이를 수행하려면 다음 명령문을 실행합니다.

    GRANT CONNECT ON HTTP ENDPOINT::hello_world_endpoint TO [DOMAIN\USER]


    Microsoft 이외의 플랫폼에서 클라이언트는 BASIC 또는 SQL Auth 중 하나를 사용하여 SQL Server에 연결할 수 있습니다. 그러나 BASIC 또는 SQL Auth를 사용하려면 채널이 보안되어야 하므로 사용자가 활성화된 SSL이 있는 포트에서만 연결할 수 있습니다.


    WSDL


    WSDL은 웹 서비스를 설명하는 XML로 작성된 문서이며, 서비스가 노출하는 서비스 및 작업(또는 메서드)의 위치를 지정합니다. WSDL은 클라이언트가 웹 서비스와 상호 작용하기 위해 필요한 정보를 제공합니다. Visual Studio .NET 및 Jbuilder와 같은 도구는 WSDL을 사용하여 클라이언트 응용 프로그램이 웹 서비스와 통신하기 위해 사용할 수 있는 프록시 코드를 생성합니다. 종점에 활성화된 WSDL이 있는 경우에는 해당 종점이 WSDL 요청을 받을 때 WSDL을 만듭니다. 이 문서의 앞부분에서 만든 종점은 인증된 요청이 종점에 전송될 때 WSDL을 만듭니다. WSDL 요청은 폼의 간단한 HTTP Get 요청입니다.


    http://servername/sql/demo?wsdl


    서버는 종점에 연결된 메타데이터를 쿼리하고 WSDL을 동적으로 생성합니다. 생성된 WSDL은 저장 프로시저 매개 변수의 풍부한 형식 설명을 제공합니다. 서버는 여러 다른 특성의 WSDL을 생성할 수 있습니다(요청/응답 메시지에서 매개 변수를 설명하기 위해 기본적인 xsd 형식을 사용하는지 또는 복잡한 형식을 사용하는지에 따라 간단한 WSDL 및 복잡한 WSDL로 칭합니다). 기본적으로는 복잡한 형식을 사용합니다.


    SOAP RPC: 메서드 호출


    위에서 만든 종점에서는 이 저장 프로시저 master.dbo.hello_world를 SOAP RPC를 통해 실행할 수 있는 웹 메서드로 노출했습니다. 다음은 HTTP를 사용하는 SOAP를 통해 이 SP를 호출하기 위해 서버에 전송된 soap 메시지의 예제입니다.


    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
    <hello_world xmlns="http://tempuri.org/">
    <msg>Hello World!</msg>
    </hello_world>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>


    결과는 다음을 포함하는 SOAP Envelope입니다.


    <SqlRowSet1 xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1">
    <row>
    <message>Hello World!</message>
    </row>
    </SqlRowSet1>


    Batches: AdHoc 쿼리


    T-SQL 명령을 사용하여 종점에서 BATCHES가 ENABLED로 설정된 경우 "sqlbatch"라고 하는 또 다른 SOAP 메서드가 종점에 암시적으로 노출됩니다. sqlbatch 메서드를 이용하면 SOAP을 통해 T-SQL 문을 실행할 수 있습니다. 이 메서드는 두 가지 매개 변수를 취합니다. 첫 번째 매개 변수는 이름이 ""이고 T-SQL 문의 배치입니다. 두 번째 매개 변수는 이름이 ""이고 선택적이며, T-SQL 문이 임의의 매개 변수를 사용한 경우 매개 변수 정보의 배열을 포함합니다. 다음은 이에 대한 예로서, sqlbatch 메서드를 호출하고 매개 변수화된 쿼리를 실행하는 SOAP 요청의 본문입니다.


    <sqlbatch xmlns="http://schemas.microsoft.com/SQLServer/2001/12/SOAP">
    <BatchCommands>
    SELECT EmployeeID, LoginID, Gender
    FROM Employee
    WHERE EmployeeID=@x
    FOR XML AUTO;
    </BatchCommands>
    <Parameters>
    <SqlParameter name="x" sqlDbType="Int" maxLength="20"
    xmlns="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlParameter">
    <Value xsi:type="xsd:string">1</Value>
    </SqlParameter>
    </Parameters>
    </sqlbatch>


    이 SOAP 요청으로부터의 응답은 다음과 같습니다.


    <sqlresultstream:SqlXml xsi:type="sqlsoaptypes:SqlXml">
    <SqlXml>
    <employees EmployeeID="1" FirstName="Nancy" LastName="Davolio"/>
    </SqlXml>
    </sqlresultstream:SqlXml>


    관리

    지금까지 종점을 만들고 종점에 대한 SOAP 요청을 전송하는 작업이 얼마나 간단한지 알아보았습니다. 이제 단 하나의 구성 요소, 즉 SQL Server만 관리하면 되므로 관리가 단순화됩니다. IIS 구성 요소를 관리할 필요가 없습니다. 종점 추상화는 IP 필터링을 활성화할 수 있는 관리자에게 더 많은 유연성을 제공합니다. 또한 종점 추상화 덕분에 http/https 웹 트래픽에 사용되는 포트를 재사용할 수 있기 때문에 또 다른 포트를 열 필요가 없습니다. 그리고 특정 사용자들에게만 CONNECT 권한을 명시적으로 부여함으로써 특정 개인에만 한하는 액세스를 위한 종점을 공급할 수 있습니다.


    결론

    Microsoft는 기본 SOAP 액세스를 이용하여 SQL Server에 액세스하기 위한 SOAP/HTTP와 같이 잘 알려지고 문서화된 표준에 기반한 프로토콜을 제공했습니다. 이로 인해 보다 광범위한 클라이언트가 SQL Server에 연결할 수 있어 상호 운용성이 촉진되고 액세스 도달이 용이해집니다.

  • 728x90

    Microsoft SQL Server 2005의 XML 옵션


    적용 대상:
    Microsoft SQL Server 2005
    Microsoft Visual Studio 2005
    Microsoft .NET Framework 2.0
    XML 및 관계형 데이터

    요약: Visual Studio 2005/SQL Server 2005 환경에서 XML 데이터 처리를 위한 세 가지 옵션을 설명하고 이 옵션들 중에서 선택하는 데 도움을 주는 사용 시나리오 및 지침을 제공합니다.


    System.Xml, SQLXML 및 XML 데이터 형식 소개


    섹션에서는 Microsoft SQL Server 2000에 제공된 XML 지원에 관한 간략한 배경 설명과 함께 XML 및 관계형 데이터 조작을 위해 Microsoft Visual Studio 2005/SQL Server 2005 환경에 제공된 세 가지 옵션의 개요를 제공합니다. 이 세 옵션은 1) System.Xml 이름 공간의 클래스, 2) SQLXML 클래스, 3) SQL Server 2005에 제공된 XML 데이터 형식입니다.

    사용자에게 다음과 같은 기능을 제공하기 위해 XML 지원이 Microsoft SQL Server 2000에 추가되었습니다.

  • 관계형 데이터를 XML로 노출
  • XML 문서를 행 집합으로 분할(Shredding)
  • XDR(XML-Data Reduced) 스키마를 사용하여 XML 스키마를 데이터베이스 스키마에 매핑하여 XML 뷰 생성
  • XPath를 사용하여 XML 뷰에서 쿼리 작성
  • HTTP를 통해 SQL Server에 데이터 노출

    이 지원은 이후의 SQLXML 웹 릴리스에서 더욱 향상되었습니다. 향상 기능은 다음과 같습니다.

  • XML 뷰에 적용된 변경 내용을 유지하기 위한 Updategrams 및 XML Bulkload
  • 매핑을 설명하기 위한 주석 달린 XML 스키마 정의 언어(XSD) 지원(여전히 XDR을 지원하지만 사용이 권장되지 않습니다.)
  • 클라이언트 쪽 FOR XML
  • SQLXML 관리 클래스
  • 웹 서비스 지원

    Microsoft .NET Framework 1.0은 XML 문서 읽기, 쓰기 및 처리를 위한 포괄적인 지원을 제공합니다. 이 지원은 다양한 XML 클래스의 성능 및 유용성을 개선하기 위해 .NET Framework 2.0에서 보다 더 강화되었습니다. .NET Framework에서 제공하는 System.Xml 이름 공간의 새 클래스는 XML 데이터를 관계형 데이터에 매핑하는 데 사용될 수 있습니다.

    SQLXML은 SQL Server 데이터베이스에 있는 관계형 데이터와 XML의 원활한 통합을 가능하게 하는 일련의 라이브러리 및 기술입니다. SQLXML은 중간 계층 구성 요소이며 FOR XML 및 OPENXML에서 제공하는 서버 쪽 XML 지원을 포함하지 않습니다. SQLXML은 관계형 원본 데이터에서 XML을 생성하고 다시 관계형 테이블에 관계형 정보를 나타내는 XML을 로드하기 위한 스키마 중심의 매핑 방법을 제공합니다. SQLXML 클래스는 XML 지원을 SQL Server 2000 데이터베이스 이상 버전에 제공합니다.

    Microsoft SQL Server 2005에는 XML 데이터 형식의 형태로 된 XML에 대한 기본 제공 지원이 추가되었습니다. XML 데이터는 XML 데이터 형식 열에 원시적으로 저장될 수 있습니다. 그 뿐만 아니라 XML 데이터 형식 열은 XML 스키마 모음을 이 열에 연결함으로써 한층 더 제약될 수 있습니다. XML 데이터 형식 열에 저장된 XML 값은 XQuery 및 XML DML(Data Modification Language)을 통해 조작될 수 있습니다. 쿼리 성능 향상을 위해 인덱스를 XML 데이터를 기반으로 구축할 수 있습니다. 또한 FOR XML 및 OPENXML이 새로운 XML 데이터 형식을 지원하도록 향상되었습니다.

    이전의 다양한 SQL Server 버전에 제공된 XML 기능과 더불어 XML 데이터를 저장 및 처리하기 위해 SQL Server 2005에 새롭게 도입된 기능은 XML 데이터를 XML 응용 프로그램에 저장하고 처리할 수 있는 여러 방법을 개발자에게 제공합니다. SQL Server 2005에서 제공하는 대체 접근 방법을 사용하여 XML 응용 프로그램을 구축할 수 있는 방법에는 여러 가지가 있으므로 올바른 선택을 할 수 있도록 다양한 기술 시나리오, 트레이드 오프 및 시너지 효과를 이해하는 것이 중요합니다. 이 문서는 SQL Server 2005에서의 XML 응용 프로그램을 개발을 위해 적합한 대안을 선택하는 데 도움이 되는 지침을 제공합니다.

    XML 사용 시나리오


    XML이 사용되는 영역은 크게 다음과 같이 분류될 수 있습니다.

  • 비즈니스 통합을 위한 XML: 엔터프라이즈 응용 프로그램 통합(EAI)으로도 알려진 비즈니스 통합은 A2A(application-to-application), B2B(business-to-business) 및 B2C(business-to-consumer) 응용 프로그램을 포함합니다. 서로 다른 시스템에서 작동하는 응용 프로그램은 XML 기반 메시지를 사용하여 서로 통신합니다.
  • 콘텐츠 관리를 위한 XML: XML에 기반한 콘텐츠 관리 시스템을 사용하여 사용자가 XML 문서를 저장, 검색, 수정 및 쿼리할 수 있습니다. 이 시스템은 XML 문서를 원시 형식으로 저장합니다.

    이제 위에서 언급한 범주에 속하는 몇 가지 시나리오를 설명하겠습니다. SQL Server 2005/Visual Studio 2005 환경에서 사용할 수 있는 여러 XML 옵션 처리에 대한 자세한 설명과 함께 이 시나리오에 대한 솔루션이 다음에 이어지는 섹션에 제공되어 있습니다.

    시나리오 1: 보험금 청구

    인터넷 상에서 서비스를 제공하는 한 자동차 보험 회사는 보험 구매자 또는 대리인이 회사의 웹 사이트를 통해 보험금 청구를 신청할 수 있도록 합니다. 이러한 청구는 본사에 있는 중앙 집중식 시스템에서 처리됩니다. 처리가 완료되면 이 시스템은 청구에 관련된 특정 정보를 특정 XML 형식으로 저장해야 합니다. 법적인 목적으로 이러한 XML 문서의 정확한 복사본이 이 시스템에서 유지 관리되어야 합니다. 이 시나리오는 콘텐츠 관리를 위한 XML 사용을 보여줍니다.


    시나리오 2: 자동차 제조업체 및 부품 공급업체 사이의 데이터 교환 I

    자동차 제조업체는 회사에 필요한 부품을 조달하기 위해 여러 부품 공급업체와 상호 작용합니다. 현재 이 제조업체는 공급업체들로부터 송장을 받습니다. 그러고 나면 이 송장에 해당하는 데이터가 기존의 송장 처리 시스템에 수동으로 전달됩니다. 송장 처리 시스템은 데이터를 관계형 형식으로 저장합니다. 이제 제조업체는 기존 송장 처리 시스템으로의 송장 데이터 전달 절차를 자동화하기를 원합니다. 이 시나리오는 비즈니스 통합을 위한 XML 사용의 예입니다.


    시나리오 3: 자동차 제조업체와 부품 공급업체 사이의 데이터 교환 II

    이 시나리오에는 이전 시나리오에서 언급한 대로 여러 부품 공급업체와 상호 작용하는 자동차 제조업체가 관련됩니다. 제조업체의 현 시스템은 공급업체가 송장의 현황을 확인하거나 제조업체의 지불 지침 복사본을 얻기 위한 기능을 제공하지 않습니다. 현재로서는 단지 전화를 통해서만 이 정보를 공급업체가 이용할 수 있습니다. 공급업체가 이 작업을 자동으로 수행할 수 있도록 자동차 제조업체는 이 정보를 웹 상에 노출할 수 있어야 합니다. 이 시나리오는 비즈니스 통합을 위한 XML의 사용을 보여줍니다.


    시나리오 4: 콘텐츠 관리 시스템

    의료, 법 및 기술 분야의 정보를 웹, 책, CD-ROM 등의 다양한 채널을 통해 고객들에게 제공하는 한 회사는 고객들에게 더 빠른 시간 내에 고품질의 콘텐츠를 전달할 수 있는 콘텐츠 관리 시스템을 구축하기를 원합니다. 이 시나리오는 콘텐츠 관리를 위한 XML의 사용을 예시합니다.


    시나리오 5: 고객 설문조사

    인터넷에서 항공권 예약 서비스를 제공하는 한 회사는 현 시즌에 고객들에게 가장 인기 있는 여행 목적지를 확인하기 위해 각 계절마다 설문조사를 실시합니다. 각 계절에 사용되는 질문은 서로 다르며 이 질문은 나중에 변경될 수 있습니다. 회사는 정보를 분석하고 분석 결과는 최대 고객 수의 요구를 충족할 수 있는 패키지 여행 정책을 설계하는 데 사용됩니다. 이 시나리오는 콘텐츠 관리를 위한 XML의 사용으로 분류될 수 있습니다.


    .NET Framework의 XML 클래스

    Microsoft .NET Framework에는 XML 기반 제품 개발을 위한 탁월한 지원 기능이 있습니다. .NET Framework에서 XmlTextReader, XmlTextWriter, XmlDocument, XmlValidatingReader 등과 같은 핵심 클래스는 모든 XML 클래스의 루트 이름 공간인 System.Xml 이름 공간에서 사용할 수 있습니다. 이들 핵심 클래스는 사용자가 스트림 기반 및 DOM 기반(문서 개체 모델 기반) 탐색/액세스 모델을 모두 사용하여 XML 문서를 읽고 쓰고 확인할 수 있게 해줍니다. System.Xml 이름 공간은 다음과 같은 하위 이름 공간을 포함합니다.

  • System.Xml.Schema - XML 스키마 정의 언어(XSD) 스키마를 다루는 클래스를 포함합니다.
  • System.Xml.Serialization - XML 형식 문서 또는 스트림으로의 개체 직렬화를 위한 클래스를 제공합니다.
  • System.Xml.XPath - Xpath 식을 사용하여 XML 문서를 탐색하기 위한 클래스를 포함합니다.
  • System.Xml.Xsl - XSLT(Extensible Stylesheet Transformations) 수행을 위한 클래스를 포함합니다.

    System.Xml 이름 공간의 향상 기능

    Visual Studio 2005에서 XsltCommand와 같은 새로운 클래스와 XmlDocument와 같은 기존 XML 클래스의 기능 향상은 XML 문서 수정, XSL 변환 적용 등을 포함하여 XML 데이터에 대해 다양한 작업을 수행하는 데 사용될 수 있습니다.

    System.Xml 이름 공간의 XML 클래스와 관련된 Visual Studio 2005의 몇 가지 향상된 기능은 다음과 같습니다.

  • XML 스키마 유효성 검사 지원이 XmlDocument 클래스에 추가되었습니다.
  • XmlReader 및 XmlWriter 클래스는 상당한 성능 개선을 제공하고 XML 스키마 형식을 지원하기 위해 향상되었습니다. 또한 생성된 형식을 구성하는 XmlReaderSettings 및 XmlWriterSettings 클래스를 사용하여 XmlReader 및 XmlWriter의 인스턴스를 만들기 위한 보다 쉬운 방법을 제공하기 위해 정적 Create 메서드가 추가되었습니다.

    System.Xml의 기능 향상에 대한 자세한 내용은 Visual Studio 2005 및 .NET Framework 2.0 릴리스를 위한 System.Xml의 새로운 기능 (영문) 백서를 참조하십시오.

    System.Xml 이름 공간의 클래스는 사용자 지정 XML 구문 분석, 조작 및 저장 논리를 구현하는 데 사용될 수 있습니다. SQL Server 2005의 공용 언어 런타임(CLR) 호스팅 기능을 활용하고 Visual Studio 2005의 XML 클래스를 사용하여 XML 처리를 중간 계층 또는 데이터베이스 계층에서 수행할 수 있습니다.

    .NET Framework XML 클래스의 사용에는 XML 문서를 데이터베이스에 [n]varchar(max) 또는 varbinary(max) 형식의 열로 또는 파일 시스템에 파일로 저장하고, System.Xml 이름 공간의 클래스를 사용하여 중간 계층 또는 데이터베이스에서 이러한 문서를 처리하는 작업이 포함됩니다. .NET Framework의 XML 클래스는 또한 XML 데이터 형식으로 저장된 데이터에 작업하는 데 사용될 수 있습니다.

    .NET Framework XML 클래스는 다음의 경우에 적합합니다.

  • 스트리밍 파서, 문서 형식 정의(DTD) 및 XSD 유효성 검사, XSLT 처리 등과 같은 모든 .NET Framework XML 기능에 액세스할 수 있기를 원합니다.
  • 단순히 XML 문서의 데이터 저장소로서 SQL Server를 사용하기를 원하며 데이터베이스 내부에 세분화된 액세스는 필요하지 않습니다.
  • .NET Framework XML 클래스를 사용하여 XML 문서의 대부분이나 전부를 처리하며 문서 수준에서 업데이트를 수행합니다.

    XML을 저장하는 데 [n]varchar(max), varbinary(max) 또는 XML 데이터 형식을 사용할 수 있습니다.

    [n]varchar(max) 또는 varbinary(max)를 사용할 경우 다음과 같은 이점을 얻을 수 있습니다.

  • 공백 및 서식 지정을 포함하여 문서의 정확한 복사본으로 XML 문서의 원문 충실도를 유지합니다.
  • 응용 프로그램은 문서 전체에 대한 삽입 및 검색 작업을 위해 가능한 가장 빠른 성능을 얻습니다.

    XML 데이터 형식 사용의 이점은 후반부의 섹션에 설명되어 있습니다.

    중간 계층에서 XML 처리 수행

    XML 처리는 .NET Framework에서 제공하는 다양한 XML 클래스를 사용하여 중간 계층에서 수행될 수 있습니다. 앞서 언급한 대로, 이 접근 방법을 채택하면 XML 문서는 데이터베이스에 [n]varchar(max) 형식 또는 XML 형식의 열로 저장되거나 파일 시스템에 파일로 저장될 수 있습니다. 중간 계층에서는 이러한 문서를 데이터베이스에서 가져와서 다음과 같이 사용자의 요구 사항에 따라 처리할 수 있습니다.

  • XML 문서를 읽어야 하는 경우 XmlReader.Create() 메서드를 통해 생성된 XmlReader를 사용하여 데이터베이스에서 얻은 문서를 로드합니다. Read()를 사용하여 문서를 탐색합니다. XmlReader 클래스는 XML 문서에 대해 가장 빠른 읽기 전용, 전진 전용의 캐시되지 않은 액세스를 제공합니다.
  • XML 문서에 대한 쓰기 액세스 권한이 필요하고 XML 데이터에 대한 완전한 탐색 액세스 권한이 요구되는 경우, XmlDocument 클래스를 사용하여 XML 문서를 로드하고 액세스합니다. XmlDocument는 .NET Framework에서 문서 개체 모델(DOM)의 구현으로서 이는 XML 문서의 탐색 및 편집을 가능하게 하는 XML 문서의 인-메모리 트리 표시입니다.
  • DTD/XSD에 기반하여 XML 문서의 유효성을 검사하거나 런타임에 XSD 정보를 얻어야 하는 경우 XmlReader 클래스를 사용합니다. XmlReaderSettings 클래스에서 true로 설정된 XsdValidation 또는 DTDValidation 중 하나를 이용하여 메서드를 만듭니다. 또한 ValidationEventHandle() 이벤트 처리기는 읽는 동안 발생한 유효성 검사 오류를 처리하도록 설정할 수 있습니다.
  • XSL 변환을 XML 문서에 적용해야 하는 경우 XslCommand 클래스를 사용하여 XML 문서를 로드하고 변환을 적용하기 위해 XPathDocument 클래스를 사용합니다. XPathDocument 클래스는 XSLT를 사용하여 XML 문서 처리를 위한 빠른 고성능 캐시를 제공합니다.
  • XPath 식을 사용하여 XML 문서를 쿼리해야 하는 경우 XPathDocument(읽기 전용) 또는 XmlDocument(읽기/쓰기)를 사용하여 XML 문서를 로드합니다. CreateNavigator() 메서드를 사용하여 XPathNavigator의 인스턴스를 만들고 인수로 필요한 Xpath 식을 XPathNavigator의 Select() 메서드에 전달합니다.

    데이터베이스에서 XML 처리 수행

    SQL Server 2005와 CLR의 통합으로 인해 개발자들은 .NET Framework에서 제공하는 XML 클래스를 사용하여 데이터베이스 계층에서도 처리를 수행할 수 있습니다. 이 통합은 .NET Framework에서 지원하는 모든 언어에서 저장 프로시저 작성, 함수, 트리거 및 사용자 정의 형식의 기능을 제공합니다. 또한 CLR 호스팅은 완전한 .NET Framework 기반 클래스 라이브러리에 대한 액세스 권한도 제공합니다. 결과적으로 앞 섹션에서 설명한 여러 XML 처리 옵션은 데이터베이스에서도 수행될 수 있습니다.

    CLR 통합 사용의 이점은 다음과 같습니다.

  • 관리되는 코드에서 데이터베이스 개체를 만들기 위해 C# 및 Visual Basic .NET과 같은 개체 지향 언어를 사용할 수 있는 기능을 제공합니다.
  • 관리되는 데이터베이스 개체는 이전 SQL Server 버전에서 사용할 수 있는 확장 저장 프로시저보다 안전합니다.
  • 사용자 정의 데이터 형식 및 사용자 정의 집계를 정의할 수 있는 기능을 제공합니다.
  • 특정 조건에서, 컴파일된 관리 데이터베이스 개체가 Transact-SQL에 대해 향상된 성능을 제공합니다.

    SQL Server 2005에서 데이터베이스 개발자는 저장 프로시저, 트리거 및 사용자 정의 함수를 위한 두 가지 옵션을 가질 수 있습니다. 이 옵션은 Transact-SQL 및 .NET Framework에서 사용할 수 있는 모든 언어(C# 또는 Visual Basic .NET)입니다. 언어의 선택은 데이터에 수행하는 작업의 종류에 따라 달라집니다. Transact-SQL은 코드가 절차적 논리를 거의 또는 전혀 사용하지 않고서 데이터 액세스를 대부분 수행하는 경우에 가장 적합합니다. 관리되는 클래스는 문자열 처리, 날짜 작업, 시스템 리소스 액세스, 파일 액세스 또는 이미지 처리와 같이 계산 집중적인 함수 및 절차에 가장 적합합니다.

    데이터베이스 계층에서 .NET Framework의 XML 클래스 사용에 따르는 단계는 다음과 같습니다.

  • 관리되는 어셈블리 개발. .NET Framework에서 사용할 수 있는 모든 언어를 사용하여 처리 기능을 어셈블리로 구현하고 이 어셈블리를 DLL로 패키지로 만듭니다. 또한 어셈블리는 다른 어셈블리를 참조할 수 있습니다.
  • 어셈블리 등록 및 사용 권한 부여. .NET Framework를 사용하여 개발한 어셈블리는 CREATE ASSEMBLY T-SQL 문을 사용하여 SQL Server에 등록될 수 있습니다. 또한 어셈블리를 등록하는 동안 어셈블리에 허용되는 코드 액세스 권한을 지정할 수 있습니다. 어셈블리는 DROP ASSEMBLY T-SQL 문을 사용하여 등록 취소할 수 있습니다.
  • T-SQL에 관리되는 형식 노출. 어셈블리에서 제공한 처리 기능은 스칼라 반환 사용자 정의 함수, 테이블 반환 사용자 정의 함수, 사용자 정의 절차(UDP) 또는 사용자 정의 트리거를 통해 T-SQL에 노출될 수 있습니다. 스칼라 사용자 정의 함수는 모든 스칼라 식에서 사용될 수 있습니다. 테이블 반환 사용자 정의 함수는 모든 FROM 절에서 사용될 수 있습니다. UDP는 EXEC 문에서 호출될 수 있습니다.

    시나리오 분석

    보험금 청구는 청구 ID, 정책 번호, 청구 중재 데이터 등과 같은 데이터 중심 정보와 사고 손해에 대한 설명과 같은 문서 중심 정보를 포함합니다. XML 문서는 데이터 중심 및 문서 중심 정보 집계에 있어 탁월합니다. 제공된 시나리오 (시나리오 1: 보험금 청구 섹션 참조)에서의 주요 요구 사항은 보험금 청구의 정확한 복사본을 XML 형식으로 유지 관리해야 한다는 점입니다. SQL Server에서는 보험금 청구를 [n]varchar(max) 또는 varbinary(max) 형식의 열로 데이터베이스에 저장하여 이 요구 사항을 쉽게 충족할 수 있습니다. 중요하지 않은 공백, 특성 순서, 이름 공간 접두사 및 XML 선언 등과 같은 정보를 보존해야 하는 경우에는 문서를 저장하는 데 XML 데이터 형식을 사용하지 않아야 한다는 점에 주의해야 합니다.


    이점

    저장소 매체로서 [n]varchar(max) 또는 varbinary(max)를 사용하고 XML 문서의 조작을 위해 System.Xml 이름 공간의 클래스를 사용하는 경우의 이점은 다음과 같습니다.

  • XML 문서의 스키마를 변경해야 하는 경우에 유연합니다. 또한 서로 다른 스키마를 사용하는 XML 문서를 동일한 열에 저장하려는 경우에도 유용합니다.
  • XML을 저장하는 데 [n]varchar(max) 또는 varbinary(max)를 사용하는 경우 XML 문서에 대한 원문 충실도를 제공합니다. 이는 보험금 청구와 같은 법적 문서를 다루는 응용 프로그램의 요구 사항이 될 수 있습니다.
  • XML 인스턴스를 파일 시스템에 파일로 저장할 때와 비교하여 트랜잭션 업데이트, 동시 액세스, 백업, 복제 등과 같은 데이터베이스 기능을 활용할 수 있습니다.
  • 이 접근 방법은 데이터베이스에서 제공하는 XML 지원에 의존하지 않으므로 응용 프로그램이 SQL Server, Oracle 등과 같은 여러 데이터베이스 서버를 지원하도록 쉽게 확장될 수 있습니다.
  • 클라이언트 시스템의 처리 능력을 사용할 수 있어 서버에서 로드가 감소합니다. CPU를 많이 사용하는 XML 처리를 중간 계층에서 수행함으로써 서버가 로드의 일부를 덜고 다른 중요한 작업에 사용될 수 있습니다.
  • 문서 수준 삽입 및 검색 작업에 최상의 성능을 제공합니다.
  • XSL 변환과 같은 복잡한 작업을 데이터베이스에서 저장 프로시저, 트리거 또는 함수로 수행할 수 있습니다.

    제한

    저장을 위해 [n]varchar(max) 또는 varbinary(max) 사용 시 및 XML 인스턴스 처리를 위해 System.Xml 이름 공간의 클래스 사용 시 제한 사항은 다음과 같이 요약될 수 있습니다.

  • XML 데이터 형식 (SQL Server 2005의 XML 데이터 형식 섹션 참조) 또는 SQLXML 옵션(SQLXML 섹션 참조)과 비교하여 코딩이 더 복잡합니다. 데이터베이스 논리가 단순하다 하더라도 중간 계층 또는 데이터베이스 계층에서 XML의 구문 분석 및 처리를 다루는 코드의 구현이 복잡해집니다.
  • 이 솔루션을 구현하는 데 필요한 코드의 양이 많습니다. 결과적으로 SQLXML 옵션과 비교하여 유지 관리 비용 또한 높습니다.
  • XML 문서가 데이터베이스에 [n]varchar(max)로 저장된 이후에는 XML 문서에서 세분화된 업데이트, 삽입 또는 삭제가 불가능합니다. 이 경우에는 쿼리 기능이 제한적입니다.
  • [n]varchar (max) 데이터 형식에 저장될 수 있는 XML 문서의 크기는 2GB로 제한됩니다.
  • 이런 식으로 저장된 문서의 열을 XML 콘텐츠를 기준으로 검색하는 것은 비용이 매우 많이 소요됩니다.

    .NET Framework에서 XML 클래스 사용의 예

    이 문서의 앞부분에서 설명한 보험금 청구 시나리오를 생각해 봅시다(시나리오 1: 보험금 청구 섹션 참조). 보험 회사는 청구가 승인되면 법적인 목적으로 청구 정보를 저장하기를 원합니다. 청구 정보는 데이터베이스에 [n]varchar(max) 데이터 형식으로 저장될 수 있습니다.

    응용 프로그램의 흐름은 다음과 같습니다.

    1. 청구 처리 후 응용 프로그램은 청구를 승인하거나 거부합니다. 2. System.Xml 이름 공간의 클래스를 사용하여 청구용 XML 문서가 생성됩니다. 3. 생성한 XML 문서는 저장 프로시저로 전달됩니다. 4. 저장 프로시저는 XML 문서를 테이블에 삽입합니다. 다음 코드 예제는 시스템에서 사용할 수 있는 청구 정보를 사용하여 XML 문서를 생성하고 XML 문서를 데이터베이스에 삽입합니다.


    using System;
    using System.Xml;
    using System.IO;
    using System.Text;
    using System.Data;
    using System.Data.SqlClient;
    namespace InsuranceClaim
    {
    class Insurance
    {
    static void Main(string[] args)
    {
    Insurance.InsertInsuranceClaim();
    }
    static void InsertInsuranceClaim()
    {
    StringWriter strWriter = null;
    XmlWriter writer = null;
    XmlWriterSettings settings = null;
    SqlConnection connection = null;
    SqlCommand command = null;
    try
    {
    strWriter = new StringWriter();
    settings = new XmlWriterSettings();
    //Use indenting for readability.
    settings.Indent = true;
    settings.Encoding = System.Text.Encoding.UTF8;
    writer = XmlWriter.Create(strWriter, settings);
    //Write the XML delcaration.
    writer.WriteStartDocument();
    writer.WriteStartElement("InsuranceClaim");
    writer.WriteStartElement("ClaimInfo");
    writer.WriteElementString("ClaimID", "C1234");
    writer.WriteElementString("ClaimType", "3");
    writer.WriteStartElement("SettlementDetails");
    writer.WriteStartElement("PaymentDetails");
    writer.WriteElementString("PaidTo", "Jeff");
    writer.WriteElementString("Amount", "2000");
    writer.WriteElementString("Date", "05/12/2002");
    writer.WriteElementString("ApprovedBy", "Mike");
    writer.WriteEndElement();//End of PaymentDetails
    writer.WriteEndElement();//End of SettlementDetails
    writer.WriteEndElement();//End of ClaimInfo
    writer.WriteStartElement("DamageReport");
    writer.WriteString("Minor accident occured on ");
    writer.WriteElementString("Address", "ABC Street, Sample City, Sample State");
    writer.WriteString(" due to ");
    writer.WriteElementString("Cause", "bad weather");
    writer.WriteString(" resulted in damage to ");
    writer.WriteElementString("DamagedItem", "Head Lights");
    writer.WriteElementString("DamagedItem", "Engine");
    writer.WriteEndElement();//End of DamageReport
    writer.WriteEndElement();//End of InsuranceClaim
    writer.WriteEndDocument();
    //Write the XML to file and close the writer.
    writer.Flush();
    connection = new SqlConnection();
    connection.ConnectionString = @"server=localhost;
    database=AdventureWorks;Integrated Security=SSPI;";
    command = connection.CreateCommand();
    command.CommandText = "InsertInsuranceClaim";
    command.CommandType = System.Data.CommandType.StoredProcedure;
    command.Parameters.Add("@CustomerID",
    System.Data.SqlDbType.Char);
    command.Parameters.Add("@Claim",
    System.Data.SqlDbType.VarChar);
    String xml = strWriter.ToString();
    string strCustomerID = "1001";
    command.Parameters[0].Value = strCustomerID;
    command.Parameters[0].Size = strCustomerID.Length;
    command.Parameters[1].Value = xml;
    command.Parameters[1].Size = xml.Length;
    connection.Open();
    command.ExecuteNonQuery();
    connection.Close();
    }
    finally
    {
    if (connection.State == ConnectionState.Open)
    connection.Close();
    if (writer != null)
    writer.Close();
    if (strWriter != null)
    strWriter.Close();
    }
    }
    }
    }


    다음은 데이터베이스 테이블을 만드는 스크립트입니다.


    CREATE TABLE [InsuranceClaim](
    [CustomerID] [char](4) NOT NULL,
    [Claim] [varchar](max) NOT NULL,
    [ModifiedDate] [datetime] NOT NULL DEFAULT (getdate())
    )


    다음 저장 프로시저는 XML 문서를 데이터베이스에 삽입하는 데 사용됩니다.


    CREATE PROCEDURE [dbo].[InsertInsuranceClaim]
    @CustomerID [char](4),
    @Claim [varchar](max)
    AS
    BEGIN
    SET NOCOUNT ON;
    INSERT INTO [InsuranceClaim] ( CustomerID, Claim )
    VALUES ( @CustomerID, @Claim )
    END;


    SQLXML


    SQL Server 2000에 도입된 SQLXML은 클라이언트 쪽의 XML 처리와 관련된 기능의 전 범위를 포괄합니다. SQLXML은 관계적으로 구조화된 데이터를 설명하는 XML을 사용하여 SQL Server 데이터베이스에서 관계형 데이터의 원활한 통합을 가능하게 하는 일련의 라이브러리 및 기술입니다.

    SQL 2000 이전에는 개발자들이 관계형 데이터와 XML 형식의 데이터 간에 상호 작용을 위한 코드 레이어를 제공해야 했습니다. 하지만 SQLXML의 등장으로 관계형 데이터와 XML 사이의 연결이 제공되었으므로 작업이 더욱 편리해졌습니다. 이 문서에 설명된 항목은 SQLXML 관리 클래스에 제한됩니다. 이 라이브러리의 다른 기능 적용 가능성에 관한 자세한 내용은 MSDN에서 SQLXML (영문) 페이지를 참조하십시오.

    SQLXML은 XML 지원을 위해 SQL Server에 도입된 많은 기능으로 구성됩니다. 이러한 기능은 다음과 같습니다.

    - 클라이언트 쪽에서 쿼리 결과를 XML로 변환하는 기능

  • 주석 달린 XSD 매핑 스키마 파일을 사용하여 관계형 데이터의 XML 뷰를 만드는 기능 - 다음을 수행할 수 있습니다.
  • XML 뷰에 대한 XPath 쿼리 정의
  • updategrams로 알려진 XML 템플릿을 사용하여 데이터베이스의 데이터에 삽입, 업데이트, 삭제 수행
  • XML 대량 로드 작업 수행

    - HTTP를 사용하여 SQL Server에 액세스하는 기능 - 다음을 수행할 수 있습니다.

  • URL에 SQL 문 지정
  • URL에 템플릿 쿼리 지정
  • URL에 템플릿 파일 지정
  • URL에 주석 달린 XSD 매핑 스키마 파일에 대해 작성된 XPath 쿼리 지정

    저장 프로시저, 사용자 정의 함수 및 템플릿 쿼리에서 제공되는 기능을 SOAP 기반 웹 서비스로 노출하는 기능 SQLXML 관리 클래스를 사용하여 SQLXML에서 제공되는 XML 기능의 이점을 활용하도록 .NET Framework에서 코드를 작성하는 기능

    클라이언트 쪽 XML 서식 설정. 클라이언트 쪽에서 FOR XML 절을 지정하면 중간 계층에서 쿼리에 대한 응답으로 서버가 반환한 행 집합에 대해 FOR XML 변환을 수행합니다. 클라이언트 쪽에서 XML 서식 설정을 수행하려면:

  • SQLXML 관리 클래스를 사용 중인 경우 SqlXmlCommand 개체의 ClientSideXml 속성을 True로 설정합니다.
  • SQLXMLOLEDB 공급자를 사용 중인 경우 ClientSideXML 공급자별 속성을 True로 설정합니다.
  • 템플릿 쿼리를 사용 중인 경우 템플릿에 client-side-xml="1"를 지정합니다.
  • HTTP를 사용하여 SQL Server에 액세스하는 경우 설정 탭의 가상 디렉터리에서 클라이언트에서 실행 옵션을 선택합니다.
  • 클라이언트 쪽에서 FOR XML과 함께 사용할 수 있는 XML 서식 설정 모드는 RAW, NESTED 및 EXPLICIT입니다. RAW 모드가 사용되면, 결과 XML 문서는 쿼리 결과의 각 행에 대한 XML 요소와 행의 각 열에 해당하는 특성을 포함합니다. NESTED 모드가 지정되면, 기본 테이블 이름이 결과 XML 문서의 요소 이름으로 반환됩니다. EXPLICIT 모드는 쿼리 자체에 원하는 XML의 형식을 지정할 수 있도록 허용함으로써 모든 형태의 XML 문서를 생성합니다.
  • XML 뷰. XML 뷰는 관계형 데이터와 XML 데이터 간의 매핑을 정의하는 주석 달린 XSD 스키마를 사용하여 생성됩니다. 이 XML 뷰는 XPath 쿼리를 사용하여 쿼리할 수 있습니다. 또한 XML 뷰를 통해 노출된 관계형 데이터를 수정한 다음 updategrams를 사용하여 수정 사항을 데이터베이스에 제출할 수 있습니다. 뿐만 아니라 XML 뷰는 COM 기반 XML Bulk Load 개체의 도움으로 대형 XML 문서를 데이터베이스에 삽입하는 데도 유용합니다.
  • SQL Server에 대한 HTTP 액세스. SQLXML은 IIS Virtual Directory Management 유틸리티로 알려진 유틸리티를 제공하며 이 유틸리티를 사용하여 HTTP를 통해 SQL Server의 XML 기능을 노출하도록 IIS 가상 디렉터리를 설정할 수 있습니다. URL에서 직접 SQL 문, 저장 프로시저, 템플릿 쿼리, 템플릿 파일 및 XPath 쿼리를 지정하기 위한 지원은 SQL ISAPI 확장 기능을 통해서 제공됩니다.
  • SQLXML의 웹 서비스 지원. SQL Server의 기능을 SOAP 기반 웹 서비스로 노출하기 위한 지원은 SQLXML 3.0에서 추가되었습니다. 이 기능을 사용하면 SQL Server가 클라이언트로부터 SOAP HTTP 요청을 받아 저장 프로시저, 사용자 정의 함수 및 템플릿을 실행할 수 있습니다.
  • SQLXML 관리 클래스. .NET Framework에서의 SQLXML 기능 액세스는 SQLXML 관리 클래스를 통해 제공됩니다. SQLXML에는 세 가지의 관리되는 클래스가 있습니다.
  • SqlXmlCommand - 데이터베이스 연결 및 쿼리 실행 측면을 다룹니다.
  • SqlXmlParameter - 쿼리에 매개 변수를 지정하도록 도와줍니다.
  • SqlXmlAdapter - .NET Framework에서 데이터 집합과의 상호 작용을 촉진합니다.

    SQLXML 관리 클래스를 사용하여 다음 작업을 수행할 수 있습니다.

  • FOR XML 절이 있는 SQL 쿼리 실행
  • 매핑 스키마에 대한 XPath 쿼리 실행
  • 템플릿 쿼리 실행
  • 템플릿 쿼리 파일 실행
  • updategram 실행
  • DiffGram 실행

    관계형 데이터를 XML 문서로 노출하기 위해 SQLXML을 사용하는 것은 다음과 같은 경우에 적합한 선택입니다.

  • 응용 프로그램이 고도로 구조화되고 관계형 테이블에 잘 매핑되는 XML 데이터를 받습니다.
  • 응용 프로그램이 외부 응용 프로그램에서 받은 대형 XML 문서를 데이터베이스에 로드해야 하고 이 문서를 관계형 형식으로 유지해야 합니다.
  • 응용 프로그램이 문서 순서를 반드시 지켜야 할 필요가 없습니다.
  • 응용 프로그램이 동일한 데이터를 여러 데이터 소비자에게 여러 다른 형식으로 제공해야 합니다.
  • DML 작업 성능이 응용 프로그램에 대단히 중요합니다.
  • 응용 프로그램이 쿼리 최적화를 위한 최적기의 모든 가능성을 활용해야 할 필요가 있습니다.
  • 응용 프로그램이 세분화된 데이터 조작을 수행합니다.
  • 응용 프로그램이 기존의 관계형 데이터를 XML로 노출해야 합니다.

    시나리오 분석

    XML 사용 시나리오에 설명된 첫 번째 데이터 교환 시나리오(시나리오 2: 자동차 제조업체와 부품 공급업체 간의 데이터 교환 I 섹션 참조)에서, 자동차 제조업체와 여러 부품 공급업체 간의 상호 작용은 SQLXML 사용의 전형적인 경우를 나타냅니다. 제조업체는 송장 데이터를 교환하기 위해 여러 공급업체들과 통신해야 합니다. 제안된 솔루션은 이 문제를 해결하기 위해 웹 서비스와 SQLXML을 사용합니다. 제조업체는 공급업체가 제조업체에게 송장을 보내기 위해 사용할 수 있는 웹 서비스를 노출합니다. 웹 서비스는 송장을 공급업체 형식에서 제조업체가 사용하는 일반적인 형식으로 변환하기 위해 고객에 따라 다른 XSLT를 사용합니다. 그런 다음 웹 서비스는 송장 문서의 내용을 관계형 테이블의 열에 매핑하는 XML 뷰를 사용하여 XML 문서를 분할합니다. 기존의 송장 처리 시스템은 관계형 테이블에서 데이터를 선택하여 처리 작업을 진행할 수 있을 것입니다. 이 시나리오의 경우 XML 뷰 사용의 이점은 다음과 같습니다.

  • 낮은 유지 관리 비용. 공급업체가 송장 스키마에 적용한 모든 변경은 공급업체에 따라 다른 XSLT 파일을 수정함으로써 쉽게 수용될 수 있습니다.
  • FOR XML과 비교하여 코딩이 덜 복잡합니다(관계형/XML 통합을 위한 서버 쪽 지원(FOR XML/OPENXML) 섹션 참조).
  • 공급업체에 대해 특정 XSLT 파일을 생성함으로써 새 공급업체를 쉽게 지원할 수 있습니다.

    이점

    SQLXML 사용의 이점은 다음과 같이 요약될 수 있습니다.

  • 관계형 데이터를 XML 데이터에 매핑하기 위해 주석 달린 매핑 스키마를 만드는 작업은 비교적 간단하며 서버 쪽의 FOR XML EXPLICIT보다 효과적으로 관리 가능한 솔루션입니다.
  • SQLXML은 FOR XML을 사용하여 만든 관계형 데이터의 읽기 전용 XML 표시와 비교하여 업데이트 가능한 양방향 XML 뷰를 만들 수 있는 기능을 제공합니다.
  • XSD 매핑은 주요 코드 변경 없이 변경 요청을 XML 형식으로 수용할 수 있는 기능을 제공합니다. 이로 인해 유지 관리가 쉬워집니다.
  • SQLXML은 클라이언트 쪽에서 XML 형식 지정을 수행할 수 있는 기능을 제공하므로 사용자가 ClientSideXML 속성을 SqlXMLCommand 클래스에 대해 true로 설정할 수 있어 서버에서 로드가 감소됩니다.

    제한s

    부정적인 면을 들자면, SQLXML이 클라이언트 쪽에서 사용되는 경우 몇 가지 제한이 있습니다.

  • XML 뷰는 XML 문서의 계층이 너무 깊거나 깊이를 알 수 없을 정도로 순환하는 경우에는 적합하지 않습니다.
  • SQLXML은 제품 카탈로그, 뉴스 보고 등과 같은 혼합된 콘텐츠 표시 및 정렬된 데이터를 포함하는 설명 문서에는 적합하지 않습니다.
  • 문서 순서가 유지되지 않기 때문에 원래의 XML 문서를 다시 작성하기가 어렵습니다.
  • XML 문서를 관계형 테이블로 세분화하면 우수한 검색 성능을 얻을 수 있지만 XML 간의 변환에는 비용이 많이 들 수 있습니다.
  • XSD 매핑 스키마에서 기본 매핑이 사용된 경우 데이터베이스 테이블 이름 및 열 정보가 노출될 수 있어 원하지 않은 정보 누출이 발생할 수 있습니다. 이러한 위험은 테이블 및 열에 대해 명시적인 매핑을 지정함으로써 피할 수 있습니다.
  • URL의 SQL 문은 신뢰할 수 있는 인트라넷에서만 사용되어야 합니다. 인터넷에서 이러한 쿼리를 사용하면 잠재적 보안 위험이 따를 수 있습니다.

    SQLXML 사용의 예

    이제 SQLXML의 개요 설명을 마쳤으니, 다음 단계로 SQLXML 관리 클래스가 적용될 수 있는 예제를 탐구해 봅시다. 특정 고객에 대한 판매 주문 정보를 내보내는 간단한 예제를 살펴보겠습니다. 이 예제에 사용되는 테이블은 AdventureWorks 데이터베이스에서 이용할 수 있습니다.

    데이터베이스의 데이터는 프레젠테이션 계층에서 표시될 수 있는 XML 형식으로 클라이언트에서 사용할 수 있어야 합니다. 그런 다음 SQL 데이터베이스의 관계형 데이터가 어떻게 SQLXML 클래스를 사용하여 XML 데이터로 조작될 수 있는지 확인하게 될 것입니다. 매핑 XML 스키마가 XML 노드 이름을 테이블 필드에 매핑하고 조작하는 데 사용됩니다. SQLXML 관리 라이브러리를 사용한 관계형 데이터 조작에 관한 자세한 내용은 MSDN에서 SQLXML (영문) 페이지를 참조하십시오.

    아래의 주석 달린 XSD 스키마는 관계형 테이블 [Sales.Customer], [Sales.SalesOrderHeader], [Sales.SalesOrderDetail]과 고객에 대한 판매 주문 정보의 대상 XML 표시 사이에 매핑을 정의합니다. 또한 아래 XSD 스키마에서 보는 바와 같이 XSD 매핑 스키마를 사용하여 XML에서의 부모(parent)-자식(child) 관계도 정의될 수 있습니다.


    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
    <xsd:annotation>
    <xsd:appinfo>
    <sql:relationship name="CustomerOrderHeader"
    parent="Sales.Customer"
    parent-key="CustomerID"
    child="Sales.SalesOrderHeader"
    child-key="CustomerID" />
    <sql:relationship name="OrderHeaderOrderDetail"
    parent="Sales.SalesOrderHeader"
    parent-key="SalesOrderID"
    child="Sales.SalesOrderDetail"
    child-key="SalesOrderID" />
    </xsd:appinfo>
    </xsd:annotation>
    <xsd:element name="Customer" sql:relation="Sales.Customer" >
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="Order" sql:relation="Sales.SalesOrderHeader"
    sql:relationship="CustomerOrderHeader" maxOccurs="unbounded" >
    <xsd:complexType>
    <xsd:sequence>
    <xsd:element name="OrderDetail"
    sql:relation="Sales.SalesOrderDetail"
    sql:relationship="OrderHeaderOrderDetail"
    maxOccurs="unbounded" >
    <xsd:complexType>
    <xsd:attribute name="SalesOrderID"
    type="xsd:integer" />
    <xsd:attribute name="ProductID" type="xsd:integer" />
    <xsd:attribute name="OrderQty" type="xsd:integer" />
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="SalesOrderID" type="xsd:integer" />
    <xsd:attribute name="CustomerID" type="xsd:integer" />
    <xsd:attribute name="OrderDate" type="xsd:date" />
    <xsd:attribute name="ShipDate" type="xsd:date" />
    </xsd:complexType>
    </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="CustomerID" type="xsd:integer" />
    </xsd:complexType>
    </xsd:element>
    </xsd:schema>
    class ExportOrders
    {
    /// <summary>
    /// This method use SqlXmlCommand class to select the records from
    /// Sales.Customer, Sales.SalesOrderHeader and Sales.SalesOrderDetail
    /// tables.The data is fetched as such from server and formatted into
    /// xml at client side. Note that the ClientSideXml is set to true.
    /// </summary>
    static void Main(string[] args)
    {
    if (args.Length < 1)
    {
    Console.WriteLine("Usage");
    Console.WriteLine("CustomerOrders <CustomerID> [OrderID]");
    return;
    }

    try
    {
    StringBuilder strBuilder = new StringBuilder();
    strBuilder.Append("/Customer[@CustomerID='");
    strBuilder.Append(args[0]);
    strBuilder.Append("']");
    if (args.Length > 1)
    {
    strBuilder.Append("/Order[@SalesOrderID='");
    strBuilder.Append(args[1]);
    strBuilder.Append("']");
    }
    SqlXmlCommand xmlCommand = new SqlXmlCommand(@"Provider=
    SQLOLEDB; Server=localhost; database=AdventureWorks;
    Integrated Security=SSPI;");
    xmlCommand.ClientSideXml = true;
    xmlCommand.RootTag = "CustomerOrders";
    xmlCommand.SchemaPath = @"CustomerOrderDetails.xsd";
    xmlCommand.CommandType = SqlXmlCommandType.XPath;
    xmlCommand.CommandText = strBuilder.ToString();
    Stream reader = xmlCommand.ExecuteStream( );
    FileStream fsOut = File.Create("CustomerOrder.xml");
    StreamWriter sw = new StreamWriter(fsOut);
    using (StreamReader sr = new StreamReader(reader))
    {
    sw.Write(sr.ReadToEnd());
    }
    sw.Flush();
    sw.Close();
    fsOut.Close();
    }
    catch (Exception exception)
    {
    Console.WriteLine( exception.ToString() );
    }
    }
    }


    위에서 제공한 방법은 명령줄 인수로 지정한 Customer ID에 대한 판매 주문 정보를 응용 프로그램으로 내보냅니다. 데이터는 클라이언트 쪽에서 XML 형식으로 변환되므로 서버 쪽에서 성능 문제를 피할 수 있습니다. 위에서 제공한 주석 달린 XSD 스키마 매핑은 CustomerOrderDetails.xsd로 저장되어야 위 코드 단편이 적절하게 작동될 수 있습니다.

    참고 데이터베이스에서 데이터를 XML로 가져오는 데 필요한 코드의 양은 예제에서 볼 수 있듯이 아주 적습니다.


    관계형/XML 통합을 위한 서버 쪽 지원(FOR XML/OPENXML)


    SELECT 문의 FOR XML 확장을 사용하여 서버 쪽에서 SQL 쿼리 결과를 XML 문서로 반환하기 위한 지원이 SQL Server에 제공되어 있습니다. 한편 OPENXML 키워드는 XML 문서에서 행 집합을 추출할 수 있는 기능을 제공합니다.


    FOR XML

    서버 쪽 FOR XML은 네 가지 XML 변환 모드 RAW, AUTO, EXPLICIT, PATH를 지원합니다.

    기본적으로, RAW 모드는 쿼리 결과 집합의 각 행을 XML 요소에 매핑하고 행의 각 열을 특성에 매핑합니다. ELEMENTS 옵션이 ROW 모드로 지정된 경우 행의 각 열은 행에 대해 생성된 요소의 하위 요소에 매핑됩니다. 또한 XMLSCHEMA 옵션을 지정하여 생성된 XML의 인라인 스키마를 요청할 수 있습니다.

    AUTO 모드는 기본적으로, SELECT 절에 최소한 하나의 열이 나열된 FROM 절의 각 테이블이 XML 요소에 매핑되고 SELECT 절에 나열된 열이 특성(또는 ELEMENTS 옵션이 지정된 경우 하위 요소)에 매핑되는 경우 중첩된 XML 요소를 생성하기 위한 지원을 제공합니다.

    EXPLICIT 모드는 쿼리 결과에서 생성된 XML의 형태를 최대한 제어할 수 있게 해줍니다. 이 모드를 이용하면 쿼리 자체에 원하는 XML에 대해 형식을 지정함으로써 어떠한 형태의 XML이라도 생성할 수 있습니다.

    EXPLICT 모드를 사용하여 복잡한 XML 문서를 작성하는 일은 번거롭습니다. PATH 모드를 중첩 FOR XML 쿼리를 작성하는 기능 및 XML 형식 인스턴스를 반환하는 TYPE 지시어와 함께 사용하면 복잡한 EXPLICIT 모드 쿼리 작성을 위한 대안을 얻을 수 있습니다. PATH 모드는 열 이름을 XPath형 구문으로 해석하여 SELECT 쿼리에서 반환한 행 집합의 열을 특성 및 하위 요소에 매핑합니다. SQL Server 2005의 FOR XML 기능 향상에 대한 자세한 내용은 Microsoft SQL Server 2005에서 FOR XML의 새로운 기능 (영문) 백서를 참조하십시오.


    OPENXML

    sp_xml_preparedocument 및 sp_xml_removedocument 시스템 저장 프로시저와 함께 OPENXML은 XML 문서의 관계형 행 집합 뷰를 제공합니다. XML 문서에서 OPENXML을 사용하려면 sp_xml_preparedocument를 인-메모리 XML 문서 표시를 만드는 데 사용해야 합니다. 이 저장 프로시저는 MSXML 파서를 사용하여 XML 문서를 구문 분석하고 OPENXML과 함께 사용할 수 있는 XML 문서에 핸들을 반환합니다. 이제 XML 문서 핸들과 같은 매개 변수, rowpattern(XML 데이터의 노드를 행에 매핑하는 XPath 식), 행 집합 스키마 및 행 집합 열과 XML 노드 간의 매핑이 OPENXML에 전달될 수 있어 행 집합을 얻을 수 있습니다. XML 문서는 더 이상 필요 없게 되면 sp_xml_removedocument 저장 프로시저를 사용하여 메모리에서 언로드되어야 합니다.


    FOR XML의 향상 기능

    FOR XML은 SQL Server 2005에서 다음과 같은 기능으로 향상되었습니다.

  • 새로운 TYPE 지시어를 사용하여 FOR XML의 결과를 형 변환하는 기능
  • FOR XML의 결과를 XML 형식의 변수에 할당하는 기능
  • XML 계층을 생성하기 위해 FOR XML 쿼리를 중첩하는 기능
  • 복잡한 XML 문서를 생성하기 위해 새로운 PATH 모드를 사용하는 기능
  • 각각 XMLDATA 및 XMLSCHEMA 옵션을 사용하여 XDR 또는 XSD 형식으로 인라인 스키마를 생성하는 기능
  • 요소 중심 XML을 생성하기 위해 RAW 모드와 함께 ELEMENTS 지시어를 사용하는 기능
  • xsi:nil="true" 특성을 가진 요소에 NULL 값을 매핑하기 위해 ELEMENT 지시어와 함께 XSINIL 옵션을 사용하는 기능

    OPENXML의 향상 기능

    SQL Server 2005에서 OPENXML은 다음과 같은 기능을 지원하도록 향상되었습니다.

  • XML 형식 데이터를 sp_xml_preparedocument에 전달하는 기능
  • WITH 절에서 새 데이터 형식을 사용하는 기능

    XML 문서를 작성 및 분리하기 위해 FOR XML 및 OPENXML을 사용하는 것은 다음의 경우에 적합한 선택입니다.

  • 응용 프로그램이 데이터를 관계적으로 저장하고 이 정보를 다른 응용 프로그램에 XML로 노출해야 합니다.
  • 응용 프로그램이 XML의 순서를 지켜야 할 필요가 없습니다.
  • 응용 프로그램이 과중한 요소 수준의 DML 작업을 수행합니다.
  • 응용 프로그램에 세분화된 데이터 액세스 및 업데이트가 필요합니다.
  • 응용 프로그램에서 웹 서비스를 통해 관계형 데이터를 노출해야 합니다.

    시나리오 분석

    시나리오 3의 요구 사항(시나리오 3: 자동차 제조업체와 부품 공급업체 간의 데이터 교환 II 섹션 참조)은 공급업체가 송장의 현황을 입수하거나 지불 지침의 복사본을 얻기 위해 이용할 수 있는 웹 서비스를 제공하는 것입니다. 웹 서비스와 함께 FOR XML은 제조업체가 인터넷에서 이러한 서비스를 노출할 수 있도록 하는 솔루션을 제공합니다. 공급업체는 웹 서비스를 이용하여 송장의 현황에 관해 문의합니다. 그러면 웹 서비스가 공급업체가 제공한 송장 ID를 이용해서 FOR XML 문을 사용하여 관계형 데이터로부터 XML 형식으로 응답을 생성합니다. 생성된 XML 문서는 공급업체에게 반환됩니다. 현재의 시나리오에서 FOR XML 문 기반 접근 방법은 다음과 같은 이점을 제공합니다.

  • FOR XML은 관계형 데이터에서 간단한 XML 문서를 동적으로 작성할 수 있는 손쉬운 방법을 제공합니다.
  • FOR XML 쿼리가 간단한 XML 문서를 작성하는 데 사용될 경우 FOR XML 쿼리의 유지 관리는 XML 뷰에 비해 쉽습니다.

    이점

    다음은 FOR XML/OPENXML 사용 시의 몇 가지 이점입니다.

  • FOR XML은 서버의 관계형 데이터로부터 XML을 생성하는 간단한 방법을 제공합니다.
  • FOR XML은 웹 서비스를 통해 비즈니스 정보를 노출할 수 있는 기능을 제공합니다.
  • OPENXML을 이용하면 행 집합을 단지 한 번의 네트워크 라운드 트립으로 대량의 INSERT, UPDATE, DELETE 작업을 수행할 수 있는 XML 형식으로 저장 프로시저에 전달할 수 있습니다.
  • FOR XML은 XSL과 함께 응용 프로그램 통합 또는 비즈니스 통합에 사용될 수 있습니다.

    제한

    XML 문서를 작성 및 분리하기 위해 FOR XML/OPENXML을 사용하는 경우의 제한 사항은 다음과 같습니다.

  • FOR XML을 EXPLICT 옵션과 함께 사용하여 XML 구조를 형성하는 것은 어려운 작업입니다.
  • FOR XML EXPLICIT를 사용하여 작성한 복잡한 쿼리를 유지 관리하기가 어렵습니다.
  • FOR XML AUTO에 의해 생성된 XML 문서는 데이터베이스 테이블 이름 및 열 정보를 노출할 수 있어 부주의한 정보 노출이 발생할 수 있습니다. 이 상황은 테이블 및 열에 별칭을 지정함으로써 방지할 수 있습니다.

    FOR XML 및 OPENXML 사용의 예

    다음 예제는 SQL Server 2005와 함께 제공되는 AdventureWorks 데이터베이스를 사용합니다. 지정된 범위의 고객에 대해 [Sales.Customer], [Sales.SalesOrderHeader], [Production.Product], [Sales.SalesOrderDetail] 테이블에서 고객, 주문 및 주문 상세 정보를 얻기 위해 FOR XML이 사용되는 예제를 살펴보도록 합시다.


    예제: FOR XML 사용

    SELECT Cust.CustomerID,
    OrderHeader.CustomerID,
    OrderHeader.SalesOrderID,
    Detail.SalesOrderID, Detail.LineNumber,Detail.ProductID,
    Product.Name,
    Detail.OrderQty
    FROM Sales.Customer Cust,
    Sales.SalesOrderHeader OrderHeader,
    Sales.SalesOrderDetail Detail,
    Production.Product Product
    WHERE Cust.CustomerID = OrderHeader.CustomerID
    AND OrderHeader.SalesOrderID = Detail.SalesOrderID
    AND Detail.ProductID = Product.ProductID
    AND (Cust.CustomerID BETWEEN 44 AND 46)
    ORDER BY OrderHeader.CustomerID,
    OrderHeader.SalesOrderID
    FOR XML AUTO


    쿼리 결과는 아래와 같습니다.


    <Cust CustomerID="44">
    <OrderHeader CustomerID="44" SalesOrderID="53575">
    <Detail SalesOrderID="53575" LineNumber="2" ProductID="952" OrderQty="2">
    <Product Name="Chain" />
    </Detail>
    <Detail SalesOrderID="53575" LineNumber="1" ProductID="969" OrderQty="1">
    <Product Name="Touring-1000 Blue, 60" />
    </Detail>
    <Detail SalesOrderID="53575" LineNumber="3" ProductID="972" OrderQty="1">
    <Product Name="Touring-2000 Blue, 54" />
    </Detail>
    </OrderHeader>
    <OrderHeader CustomerID="44" SalesOrderID="59024">

    <Detail SalesOrderID="59024" LineNumber="1" ProductID="972"
    OrderQty="3">
    <Product Name="Touring-2000 Blue, 54" />
    </Detail>
    <Detail SalesOrderID="59024" LineNumber="2" ProductID="957" OrderQty="2">
    <Product Name="Touring-1000 Yellow, 60" />
    </Detail>
    </OrderHeader>
    </Cust>
    <Cust CustomerID="46">
    <OrderHeader CustomerID="46" SalesOrderID="48354">
    <Detail SalesOrderID="48354" LineNumber="1" ProductID="730" OrderQty="1">
    <Product Name="LL Road Frame - Red, 62" />
    </Detail>
    </OrderHeader>
    </Cust>


    다음 예제는 OPENXML 및 XPath 식을 사용하여 XML 문서에 지정된 주문 상세 정보를 추출합니다.


    예제: OPENXML 사용

    DECLARE @XmlDocumentHandle int
    DECLARE @XmlDocument nvarchar(max)
    SET @XmlDocument = N'<ROOT>
    <Cust CustomerID="44">
    <OrderHeader CustomerID="44" SalesOrderID="53575">
    <Detail SalesOrderID="53575" LineNumber="2" ProductID="952" OrderQty="2">
    <Product Name="Chain" />
    </Detail>
    <Detail SalesOrderID="53575" LineNumber="1" ProductID="969" OrderQty="1">
    <Product Name="Touring-1000 Blue, 60" />
    </Detail>
    <Detail SalesOrderID="53575" LineNumber="3" ProductID="972" OrderQty="1">
    <Product Name="Touring-2000 Blue, 54" />
    </Detail>
    </OrderHeader>
    <OrderHeader CustomerID="44" SalesOrderID="59024">
    <Detail SalesOrderID="59024" LineNumber="1" ProductID="972" OrderQty="3">
    <Product Name="Touring-2000 Blue, 54" />
    </Detail>
    <Detail SalesOrderID="59024" LineNumber="2" ProductID="957" OrderQty="2">
    <Product Name="Touring-1000 Yellow, 60" />
    </Detail>
    </OrderHeader>
    </Cust>
    <Cust CustomerID="46">
    <OrderHeader CustomerID="46" SalesOrderID="48354">
    <Detail SalesOrderID="48354" LineNumber="1" ProductID="730" OrderQty="1">
    <Product Name="LL Road Frame - Red, 62" />
    </Detail>
    </OrderHeader>
    </Cust>
    </ROOT>'
    -- Create an internal representation of the XML document.
    EXEC sp_xml_preparedocument @XmlDocumentHandle OUTPUT, @XmlDocument
    -- Execute a SELECT statement using OPENXML rowset provider.
    SELECT *
    FROM OPENXML (@XmlDocumentHandle, '/ROOT/Cust/OrderHeader/Detail',2)
    WITH (CustomerID varchar(10) '../@CustomerID',
    OrderID int '../@SalesOrderID',
    LineNumber int '@LineNumber',
    ProductID int '@ProductID',
    Quantity int '@OrderQty')
    -- Remove the internal representation.
    EXEC sp_xml_removedocument @XmlDocumentHandle


    쿼리 결과는 아래와 같습니다.

    --------------------------------------------------------
    CustomerID   OrderID   LineNumber   ProductID   Quantity
    --------------------------------------------------------
    44      53575      2      952      2
    44      53575      1      969      1
    44      53575      3      972      1
    44      59024      1      972      3
    44      59024      2      957      2
    46      48354      1      730      1
    --------------------------------------------------------

    SQL Server 2005의 XML 데이터 형식


    XML 데이터의 계층적 특성으로 인해 데이터의 구조가 복잡해질수록(예: 계층 깊이의 증가) XML 데이터를 관계형 데이터로 모델링하기가 어려워집니다. 게다가 XML 데이터가 관계형 데이터에 매핑될 때 XML 인스턴스에 있는 요소의 순서가 유지되지 않으며 분리된 관계형 데이터에서 원래의 XML 문서를 작성하는 데 관련하여 많은 비용이 듭니다. XML 데이터를 저장하기 위한 관계형 모델의 제한 사항 때문에 XML 인스턴스를 원시적으로 저장하는 것이 가장 좋습니다. 원시 XML 인스턴스는 관계형 모델의 제한 사항에 영향을 받지 않으며 계층 구조 데이터 또는 중첩 데이터를 처리하는 기능, 요소의 순서를 유지하는 기능, XML 데이터를 저장 및 검색하는 간편한 방법, 다중 스키마를 지원하는 유연성 등과 같은 기능을 제공합니다.

    Microsoft SQL Server 2005는 XML 데이터 처리를 위한 광범위한 지원을 제공합니다. SQL Server 2005에서는 XML 값이 XML 데이터 형식 열에 원시적으로 저장될 수 있어 XML 스키마의 모음에 따라 형식화되거나 혹은 형식화되지 않은 상태로 있을 수 있습니다. 세분화된 데이터 조작은 XQuery 및 XML DML을 사용하여 지원되며 후자의 경우는 데이터 수정을 위한 확장입니다. 게다가 XML 열은 쿼리 성능 향상을 위해 인덱스될 수 있습니다.


    형식화된 XML

    형식화된 XML은 XML 데이터를 설명하는 XML 스키마가 있는 경우에 이상적입니다. 이와 같은 경우 XML 스키마 모음을 XML 열에 연결하여 형식화된 XML을 제공할 수 있습니다. XML 형식 열에 대한 유효성 검사는 열과 연결된 XML 스키마 모음을 기준으로 수행됩니다. 또한 형식화된 XML 데이터는 노드 값의 런타임 변환이 필요하지 않으므로 형식화된 XML 데이터를 포함하는 쿼리의 성능은 형식화되지 않은 XML 데이터와 비교하여 더 우수합니다.


    형식화되지 않은 XML

    형식화되지 않은 XML의 사용은 스키마가 있지만 서버에서 데이터 유효성을 검사하기를 원하지 않는 경우 또는 사용할 수 있는 스키마가 없는 경우에 적합합니다. 다음과 같은 경우에는 스키마가 제공되어 있어도 형식화되지 않은 XML을 저장할 수 있습니다.

  • 응용 프로그램에 고정 스키마가 없습니다.
  • 서버에서 데이터를 저장하기 전에 응용 프로그램이 클라이언트 쪽에서 유효성 검사를 수행합니다.
  • 응용 프로그램이 스키마에 준하여 유효하지 않은 XML 데이터를 일시적으로 저장합니다.
  • 응용 프로그램이 서버에서 지원되지 않는 스키마 구성 요소(예: key/keyref)를 사용합니다.

    형식화되지 않은 XML 문서는 어떤 스키마와도 연결되지 않은 경우에라도 제대로 형성될 수 있도록 검사됩니다. 형식화되지 않은 XML은 노드 값의 런타임 변환으로 인해(노드 값이 내부적으로 유니코드 문자열로 저장되기 때문), 성능 손실을 초래한다는 점에 주의하십시오.

    XML 데이터 형식의 사용 시나리오

    SQL Server 2005의 새로운 XML 데이터 형식을 사용하면 이제 다음과 같은 작업을 할 수 있습니다.

  • 관계형 열뿐 아니라 XML 형식의 열이 하나 이상인 테이블을 만듭니다.
  • XML 열을 XML 스키마 모음에 연결하여 형식화된 XML 열 형식을 만듭니다.
  • 업무 규칙을 유지하기 위해 다른 XML을 포함하는 XML 열 또는 비 XML 형식 열에 제약 조건을 만듭니다.
  • XML 데이터 형식의 인스턴스를 저장하는 데 사용할 수 있는 XML 형식의 변수를 만듭니다.
  • 저장 프로시저 또는 사용자 정의 함수에 XML 형식의 매개 변수를 만듭니다.
  • 사용자 정의 함수에서 XML 형식 값을 반환합니다.
  • 새로운 TYPE 지시어를 사용하여 얻은 FOR XML 쿼리 결과를 XML 형식의 변수에 할당합니다.
  • XQuery의 하위 집합을 실행하여 XML 구조로 쿼리하고 XML 데이터를 변환합니다.
  • XML 형식의 열을 기반으로 계산 열을 만듭니다.
  • 쿼리 성능을 향상시키기 위해 XML 형식의 열에 XML 인덱스를 만듭니다.
  • XML DML을 사용하여 XML 인스턴스에서 요소 수준의 삽입, 삭제, 업데이트 작업을 수행합니다.
  • XML 형식 데이터의 인스턴스를 sp_xml_preparedocument로 전달하여 인-메모리 XML 문서 표시를 준비합니다.
  • XQuery 및 XML DML을 사용하여 관계형 열 및 XML 열을 모두 포함하는 도메인 간 쿼리를 작성합니다.
  • 각각 CAST 및 CONVERT를 사용하여 XML 형식을 varchar 또는 nvarchar 형식으로 캐스팅하거나 변환합니다.
  • CAST 또는 CONVERT를 사용하여 [n]varchar, [n]text, varbinary, image와 같은 문자열 데이터 형식을 XML 형식으로 변환하거나 캐스팅합니다.

    XML 데이터 형식 메서드 및 XML DML

    XML 데이터 형식 열에 대한 쿼리 및 조작은 다섯 가지 메서드를 통해 지원됩니다. XML 문서의 조각은 XML 데이터 형식의 query() 메서드를 사용하여 추출될 수 있습니다. query() 메서드는 XQuery 식을 인수로 받아들여 형식화되지 않은 XML 인스턴스를 반환합니다. 스칼라 값은 XQuery 식과 반환되기를 원하는 SQL 형식을 지정하여 value() 메서드를 사용하여 XML 인스턴스에서 추출될 수 있습니다. XML 인스턴스에 대한 존재 확인은 exist() 메서드를 사용하여 수행될 수 있습니다. XML 문서를 관계형 데이터로 분해하는 작업이 nodes() 메서드를 통해 용이해집니다.

    데이터 조작 작업은 modify() 메서드를 사용하여 XML 인스턴스에서 수행될 수 있습니다. XML DML 지원은 XQuery에 추가된 삽입, 삭제, 업데이트 키워드를 통해 제공됩니다. 삽입, 삭제, 업데이트 키워드를 각각 사용하여 하나 이상의 노드가 삽입, 삭제 및 업데이트될 수 있습니다.


    XML 인덱싱

    XML 데이터 형식 열에서 쿼리 처리 작업에 포함되는 구문 분석 및 분할은 XML 인스턴스의 크기가 매우 큰 경우 엄청나게 긴 시간이 소비될 수 있습니다. XML 데이터 형식의 쿼리 성능은 이러한 열에 인덱스를 생성함으로써 향상될 수 있습니다. XML 데이터의 크기 및 사용 시나리오는 필요한 인덱스의 종류를 지정하는 데 중요한 역할을 수행합니다. SQL Server는 두 가지 종류의 인덱스, 즉 주 XML 인덱스와 보조 XML 인덱스를 지원하며 후자는 전자 없이는 존재할 수 없습니다.

    XML 열에서 주 XML 인덱스의 생성은 XML BLOB를 분할하고 이 값을 내부 테이블에 저장합니다. 이로 인해 런타임에 수반되는 분할 작업이 없어져 쿼리 실행 시간 동안 성능이 향상되었습니다. 사용 시나리오에 따라 보조 XML 인덱스를 생성함으로써 쿼리 성능을 한층 더 향상시킬 수 있습니다. 각각 경로, 속성, 값을 기준으로 쿼리 성능을 향상시키기 위해 보조 XML 인덱스의 세 가지 유형인 PATH, PROPERTY, VALUE를 만들 수 있습니다. XML 형식 열에 적합한 보조 인덱스 선택에 관한 자세한 내용은 “XML 데이터 형식을 위한 성능 최적화” 백서에서 볼 수 있습니다.

    XML 문서를 XML 데이터 형식으로 저장하는 것은 다음의 경우에 적합한 선택입니다.

  • 응용 프로그램이 XML 인스턴스의 Infoset 콘텐츠를 보존하도록 요구됩니다. XML 문서의 Infoset 콘텐츠는 문서 계층, 요소 순서, 요소 및 특성 값 등을 포함합니다. 특성 순서, 이름 공간 접두사, 중요하지 않은 공백 및 XML 선언과 같은 정보는 보존되지 않습니다.
  • 응용 프로그램이 XML 문서에 대한 요소 수준의 수정 및 쿼리 작업을 필요로 합니다.
  • 응용 프로그램이 쿼리 처리 속도를 높이기 위해 XML 데이터 형식 열에 대한 인덱스를 필요로 합니다.
  • XML 데이터에 스키마가 있을 수도 있고 없을 수도 있습니다.
  • 응용 프로그램이 다양한 구조의 XML 문서 또는 관계형 구조에 매핑하기에 너무 어려운 서로 다르거나 복잡한 스키마를 따르는 XML 문서를 사용합니다.

    시나리오 분석: 콘텐츠 관리 시스템

    이제 XML 사용 시나리오(시나리오 4: 콘텐츠 관리 시스템 섹션 참조)에서 설명한 콘텐츠 관리 시스템을 분석해 봅시다. 출판 회사는 텍스트, 이미지, 오디오, 비디오 등 다양한 형식의 정보를 처리합니다. 독립적으로 사용될 수 있는 정보 블록은 다양한 소스에서 수집되고 데이터베이스에서 유지 관리됩니다. 이러한 정보 블록은 구성 요소로 알려져 있습니다. 개별 구성 요소를 조합하여 문서가 만들어집니다. 문서에 어떤 구성 요소가 포함되는지는 사용자의 요구에 따라 다릅니다. 이러한 문서는 다양한 채널을 통해 가입한 사용자들에게 제공됩니다. 콘텐츠 관리 시스템은 일반적으로 높은 성능과 확장성을 갖추고 콘텐츠를 저장, 조회, 검색 및 업데이트할 수 있는 능력이 있어야 합니다.

    통합 데이터 모델인 XML은 동일한 문서에 XML 데이터 및 XML 콘텐츠를 모두 저장할 수 있는 뛰어난 옵션을 제공합니다. XML은 또한 데이터에서 표시를 분리하는 기능을 제공하는데, 이 기능은 동일한 정보가 각 사용자마다 서로 다르게 표시될 수 있기 때문에 중요합니다. SQL Server 2005에서 제공하는 원시 XML 데이터 형식을 사용하여 이러한 콘텐츠 관리 시스템의 요구 사항을 맞출 수 있습니다. XML 데이터 형식을 사용하면 XML 문서를 저장하고, XML DML을 사용하여 요소 수준에서 XML 문서를 수정하고 XQuery를 사용하여 XML 문서에서 쿼리를 수행할 수 있습니다.


    시나리오 분석: 고객 설문조사

    고객 설문조사 XML 사용 시나리오(시나리오 5: 고객 설문조사 섹션 참조)에서 주요 요구 사항은 여러 스키마를 사용하는 설문조사 정보를 저장할 수 있는 능력입니다. 고정 스키마가 없는 데이터는 관계형 테이블을 하나만 사용하여 모델링될 수 없습니다. XML 열이 있는 관계형 테이블은 이러한 정보를 저장할 수 있는 우수한 옵션을 제공합니다. 설문조사의 형식을 저장하기 위해 추가 열을 관계형 테이블에 추가할 수 있습니다. 설문조사 형식 열을 사용하여 설문조사 형식의 모든 레코드를 반입함으로써 설문조사의 특정 형식에 해당하는 정보를 분석할 수 있습니다. 일반적인 설문조사에서 고객은 대개 모든 질문에 대해 대답하지는 않습니다. 따라서 몇 개의 열을 만들고(설문조사에서 각 질문마다 하나씩) 대답하지 않은 질문에 대해 데이터베이스에 NULL 값을 저장하는 대신, 하나의 열에 고객 한 명에 대한 설문조사 정보를 XML로 저장하는 것이 유용할 수 있습니다. 이 시나리오의 경우에는 다음과 같은 이유 때문에 고객 설문조사 정보를 XML 형식 열로 저장하는 것이 더 적절합니다.

  • XML 형식 열을 사용하면 서로 다른 스키마를 사용하는 설문조사 정보를 단일 XML 형식 열에 저장할 수 있습니다. XML 형식 열을 XML 스키마 모음에 연결하면 사용자가 여러 설문조사 형식의 데이터를 저장할 수 있습니다.
  • 설문조사 정보의 유효성 검사를 사용자 인터페이스에서 수행할 수 있으며 데이터베이스 수준에서 유효성 검사를 실시하기 위해 이 정보를 형식화된 XML 데이터 형식 열로 저장할 필요가 없습니다.
  • XQuery를 사용하여 데이터를 분석할 수 있습니다.

    이점

    XML 데이터를 XML 데이터 형식 열에 저장할 경우의 이점은 다음과 같이 요약될 수 있습니다.

  • XML 데이터 형식은 문서 순서 및 문서 구조를 유지하면서 서버에서 XML 데이터를 저장하는 간단하고 손쉬운 방법을 제공합니다. 이는 문서 순서 및 문서 구조가 매우 중요한 문서의 경우에 특히 그렇습니다. 응용 프로그램이 일부 데이터 원본에서 XML 문서를 가져와서 그 문서를 저장하도록 작동하는 간단한 시나리오를 가정해 봅시다. 문서를 nvarchar 또는 text 열에 저장하면 XML이 잘 구성되도록 보장되지 않으며 문서 내용에 쉽게 액세스할 수 없습니다. 이와 같은 경우에는 들어오는 XML 문서를 XML 열에 원시적으로 저장하는 것이 적절합니다.
  • XML 데이터 형식은 XML 데이터에서 세분화된 쿼리를 수행하고 작업을 수정할 수 있는 능력을 제공합니다. SQL Server 2005 이전에는 XML을 데이터베이스에 원시적으로 저장하는 방법이 없었습니다. 따라서 XML 데이터를 수정하거나 쿼리하려면, nvarchar 또는 text 열에서 데이터를 로드해서 문자열을 이용하여 XML 문서를 만든 다음 수정해야 했습니다. 데이터베이스에 다시 데이터를 기록하고 수정하기 위해서도 이와 비슷한 단계를 거쳐야 했습니다. 이제 XML 데이터 형식을 사용하면 이러한 작업이 훨씬 쉬워집니다.
  • XML 데이터 형식을 사용하면 더 빠른 쿼리 처리를 위해 XML 데이터 형식 열에 인덱스를 만들 수 있습니다.
  • XML 데이터 형식을 사용하는 경우 XML 스키마 모음과 XML 데이터에 대한 제약 조건을 사용하여 업무 규칙을 유지할 수 있습니다. XML 스키마는 데이터 유효성을 검사하고, 형식 기반 작업 상의 의미론을 추가하고, 쿼리 및 데이터 수정 문의 컴파일 동안 형식화되지 않은 XML보다 더 정확한 형식 검사를 수행하고 저장 및 쿼리 처리를 최적화합니다.
  • XML 형식 데이터는 데이터베이스에 저장되므로 백업 및 복원, SQL Server 보안, 트랜잭션, 로깅 등과 같은 다양한 데이터베이스 작업에 포함됩니다.

    제한

    다음은 새로운 XML 데이터 형식으로 작업할 경우에 알아두어야 할 제한 사항들 중 일부입니다.

  • 데이터의 정확한 복사본이 저장되지 않습니다. 중요하지 않은 공백, 이름 공간 접두사, 특성 순서 및 XML 선언이 보존되지 않습니다.
  • XML 문서에서 최대 계층 깊이는 128입니다.
  • XML 문서의 내부 이진 표시의 최대 크기는 2GB입니다.
  • XML 인스턴스를 비교할 수 없습니다. 따라서
    • XML 열이 기본 키 또는 외부 키 제약 조건의 일부가 될 수 없습니다.
    • XML 열을 GROUP BY 문에서 그룹화 값으로 사용할 수 없습니다.
  • text, ntext, image 데이터 형식은 SQL Server 2005에서 사용이 지양되므로 XML은 이러한 형식으로 캐스팅될 수 없습니다. 하지만 XML 데이터 형식은 [n]varchar 및 [n]varbinary 형식으로 캐스팅될 수 있습니다.

    XML 데이터 형식 사용의 예

    예제 응용 프로그램은 AdventureWorks 데이터베이스에서 Sales.Store 테이블을 사용합니다. Sales.Store 테이블은 기본 키로 CustomerID를, XML 열로 Demographics를 포함합니다. Demographics 열은 Store Survey 정보를 포함합니다. Store Survey의 일부로 저장된 정보는 선택적입니다. 이는 Demographics 열이 모든 요소를 포함할 수도 있고 그렇지 않을 수도 있음을 의미합니다. 동일한 정보가 관계형 형식으로 저장된 경우에는 이들 요소를 테이블의 열로 만들어야 합니다. Store Survey 정보의 대부분은 선택적이기 때문에 이 열은 데이터의 대부분에 대해 NULL 값을 포함할 것입니다. 이러한 낭비를 피하기 위해 Demographics 열에 있는 Store Survey 정보는 XML 형식으로 저장됩니다. Demographics 열은 판매 정보 즉, 각 고객에 대한 연간 판매, 연간 수익, 은행 이름 등을 포함합니다. 이들 필드는 데이터의 XML 요소로 저장됩니다.

    예제 응용 프로그램은 다음 기능을 수행합니다.

  • 모든 고객의 목록을 인구 통계학적 정보와 함께 표시합니다.
  • 특정 고객에 대한 인구 통계학적 정보를 표시합니다.
  • Sales.Store 테이블에 새 고객을 인구 통계학적 정보와 함께 삽입합니다.
  • 해당 고객에 대한 인구 통계학적 정보의 일부 요소(예: Annual Sales, Annual Revenue 등)를 수정합니다.
  • 해당 고객에 대한 인구 통계학적 정보를 삭제합니다.

    응용 프로그램이 System.Data.SqlTypes.SqlXml 클래스를 사용하여 XML 열에서 데이터를 검색합니다. SQLXML 클래스는 XML 열에 대한 직접적인 매핑입니다.

    SQLXML 클래스를 사용하면 어떤 매핑이나 변환 없이도 XML 열에서 데이터를 직접 검색할 수 있습니다.

    여기에서 customer ID 12에 대한 Annual Revenue 요소를 검색하는 방법의 예를 살펴보도록 합시다. 다음 코드 예제는 위에서 언급한 두 번째 기능을 보여줍니다.

    Public void RetreiveAnnualRevenue ()
    {
    SqlConnection conn = new SqlConnection();
    conn.ConnectionString = @"Server=localhost; Database=AdventureWorks;
    integrated security=SSPI";
    conn.Open();
    SqlCommand command = conn.CreateCommand();
    command.CommandText = @"select Demographics.query(
    'declare namespace SS="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey"
    <StoreInfo>

    <AnnualRevenue>
    {data(/SS:StoreSurvey/SS:AnnualRevenue)}
    </AnnualRevenue>
    </StoreInfo>') as
    Result from Sales.Store where CustomerID=12";
    SqlDataReader datareader = command.ExecuteReader();
    System.Text.StringBuilder builder = new System.Text.StringBuilder();
    While (datareader.Read())
    {
    SqlXml sqlxml = datareader.GetSqlXml(0);
    builder.Append(sqlxml.Value);
    }
    //Note: xml1 is a XML web control
    this.xml1.DocumentContent = builder.ToString();
    this.xml1.TransformSource = @"StoreInfo.xslt";
    }


    위 메서드에서는 AdventureWorks 데이터베이스에 SqlConnection 개체의 인스턴스를 만듭니다. 명령 텍스트 속성은 customer ID 12에 대한 Annual Revenue 필드의 값을 검색하는 쿼리를 포함합니다.

    이 코드는 XML 열에서 직접적으로 실행되는 XQuery 입니다. 쿼리의 반환 값은 XML 조각으로서 SQLXML 클래스에 매핑됩니다. 그러고 나서 XML 조각은 SQLXML 클래스의 Value 속성을 사용하여 검색될 수 있습니다.

    그런 다음 검색된 XML 조각은 XML 웹 서버 컨트롤을 사용하여 클라이언트 응용 프로그램에 표시됩니다.


    다양한 접근 방법 비교

    기능 .NET Framework의 XML 클래스 FOR XML / OPENXML SQLXML XML 데이터 형식
    코드 복잡도 높음. XML 데이터와 관계형 데이터 사이를 직접적으로 매핑하는 클래스가 없습니다. 보통. FOR XML EXPLICIT를 사용한 쿼리 작성이 어렵습니다. 낮음. 클래스가 관계형 데이터를 XML 데이터로 조작하는 메커니즘을 제공하고 또한updategrams가 레코드를 업데이트하는 기능을 제공합니다. 낮음. XML 데이터가 그 자체로서 열에 저장되므로 복잡도가 줄어듭니다. 뿐만 아니라 Visual Studio 2005는 XML 데이터 형식을 조작하기 위한 클래스를 제공합니다. XML DML은 XML 데이터를 수정하는 데 사용할 수 있습니다.
    유지 관리성 복잡함. 테이블의 필드 또는 XML을 변경하는 데 코드 변경이 필요합니다. 어려움. 테이블의 필드 또는 XML을 변경하는 데 쿼리 변경이 필요합니다. 쉬움. 대부분의 경우 매핑 XSD 파일을 수정하면 변경 사항이 적용될 수 있습니다. 쉬움. XQuery는 데이터베이스의 XML 열 쿼리를 위한 쉬운 구문을 제공합니다.
    설치 .NET Framework 이외의 다른 특별한 설치가 필요하지 않습니다. 특별한 설치가 필요하지 않습니다. SQLXML 라이브러리가 클라이언트 시스템에 설치되어 있어야 합니다. 특별한 설치가 필요하지 않습니다.
    보안 정상적인 경우에는 데이터 유형 및 형식이 클라이언트 쪽에 노출되지 않기 때문에 상당히 안전합니다. 테이블 이름 및 열 이름의 노출을 방지하도록 적절한 주의를 기울인다면 안전합니다. 매핑 XSD 파일이 중간 계층 대신 클라이언트 쪽에 저장된 경우 매핑 XSD 파일의 보안을 유지하도록 설계해야 합니다. 보안됨
    .NET Compact Framework에 대한 지원 제한된 지원. Microsoft .NET Compact Framework에서 XmlDataDocument는 지원되지 않습니다. 지원됨 지원되지 않음 지원되지 않음. SQL Server의 XML 데이터 형식 열이 SQL Server 모바일에 동기화되면 ntext로 변환됩니다.
    데이터 유효성 검사 클라이언트 및 서버에 의해 실행될 수 있습니다. 서버에 의해 실행될 수 있습니다. 클라이언트에 의해 수행될 수 있습니다. XML 스키마를 사용하여 서버에 의해 실행될 수 있습니다.
    데이터 저장소 [n]varchar(max), XML, or varbinary(max) 관계형 테이블(XML을 필드로 사용할 수 있음) 관계형 테이블(XML을 필드로 사용할 수 있음) XML 데이터 형식
    충실도 원문 충실도(바이트 수준에서 XML 데이터를 보존함) 관계 충실도(데이터의 계층 구조는 보존하지만 요소 간의 순서는 무시함) 관계 충실도 InfoSet 충실도(XML 데이터의 InfoSet 콘텐츠를 보존함)
    저장소에서의 데이터 액세스 및 업데이트 문서 수준에서 업데이트를 지원합니다. 세분화된 데이터 액세스 및 업데이트를 지원합니다. 세분화된 데이터 액세스 및 업데이트를 지원합니다. 세분화된 데이터 액세스 및 업데이트를 지원합니다.


    결론

    이 문서에서는 SQL Server 2005에서 XML을 처리하기 위한 여러 옵션에 대해 알아보았습니다. System.Xml 이름 공간, SQLXML 및 XML 데이터 형식을 각각의 관련 이점 및 제한과 함께 예제 시나리오를 들어 설명했습니다. 이상적인 시나리오에서 나타난 성과를 통해 사용자는 자신의 응용 프로그램에 맞는 적절한 XML 옵션을 선택할 수 있습니다.


    추가 정보

    - Microsoft SQL Server 2005 웹 사이트
    - Microsoft SQL Server 2005에서의 XML 지원
    - Microsoft SQL Server 2005를 위한 최상의 XML 사용 방법
    - Microsoft SQL Server 2005에서 FOR XML의 새로운 기능
    - XML 데이터 형식을 위한 성능 최적화


  • 728x90

    제 1강 : 파티션드 테이블


        전문가 강좌: 김민석

    테이블을 만들고 데이터를 적제하고 어떻게하면 할당된 PGAE 에 많은 데이터를 넣어서 효율적으로 관리할까?

    테이블 사이즈가 40G ~ 80G 가 되고 있는데, 풀스캔을 한번 타면 어떻게 될까? 생각만 해도 아찔한 순간입니다.. 모 사이트의 어떤서버를 통합하니 1 TERA 테이블이 생겼다. 인덱스 리빌딩을 해야 하는데... 이럴경우 관리자가 떠 안아야 할 부담은 거의 절정을 이루게된다. 이러한 대용량 테이블을 보다 효과적으로 관리하기 위해서 SQL 2005 에서는 PARTITIONED TABLE 이라는 기술을 선보이고 있습니다. 모두 아는 기능이고 해서 특별히 시간내기 어렵고, 기능을 실습해 보고 싶은 분을 위해서 간단하게 따라하기 식으로 글을 적어 보았습니다.

    이미 다른 상용 DBMS 들은 다양한 테이블 분할방법 (RANGE PARTITIONED TABLE, LIST PARTITIONED TABLE, HASH PARTITIONED TABLE)을 선보이며, 실제로 운영하고 있습니다. 늦은 감이 없지는 않지만, 언제까지 2008년에 제품지원이 중단되는 2000만을 MSSQL 이라고 생각하고 있는 사람들도 우끼는 이야기 라고 생각합니다. 어떤 데이터베이스가 가장 멋진 데이터베이스가 아니라, 해당 제품을 가장 잘 핸들링 하는 사람이 있는 데이터베이스가 가장 좋은 데이터베이스 라고 개인적으로 생각합니다.

    SQL 2000 엔터프라이즈를 쓰면서 SCHEMA BINDING INDEXED VIEW 도 모르고, MSCS 도 써보지 않았다면?? 혹자는 어려워 못쓸 것이고, 혹자는 들어보지 못해서 못 쓸 것입니다. 간단하게 실습하고 긴요하게 쓰이길 바랍니다.

    우리가 꿈꾸고 갈망하던 많은 부분이 MSSQL 2005 를 통해서 해결되어 가고 있고, 더해서 차기 활화산(카트마이)도 멋지게 타고 있으니 열심히 공부해 보도록 하자.

    목차

    PARTITIONED TABLE
         PARTITIONED FUNCTION
         PARTITIONED SCHEME
         CREATE PARTITIONED TABLE
         CHECK PARTITIONED INFORMATION
         PARTITION MERGE
         PARTITION SPLIT
         PARTITION SWITCH

    EXAMPLE
         TO IMPLEMENT AN AUTOMATIC SLIDING WINDOW IN A PARTITIONED TABLE

    ON SQL 2005
         http://msdn2.microsoft.com/en-us/library/aa964122.aspx

    USE MASTER
    GO

    -- DROP DATABASE PDB
    -- GO

    /*-----------------------------------------------------------------------
    PARTITIONED TABLE
          PARTITIONED FUNCTION
          PARTITIONED SCHEME
          CREATE PARTITIONED TABLE
          CHECK PARTITIONED INFORMATION
          PARTITION MERGE
          PARTITION SPLIT
          PARTITION SWITCH

          * PARTITIONED FUNCTION
    분할된 테이블 및 인덱스를 만드는 첫번째 단계로 CREATE PARTITION FUNCTION 이다.
    -------------------------------------------------------------------------*/

    CREATE PARTITION FUNCTION PARTITION_FUNCTION_NAME ( INPUT_PARAMETER_TYPE )
    AS RANGE [ LEFT | RIGHT ]
    FOR VALUES ( [ BOUNDARY_VALUE [ ,...N ] ] )
    [ ; ]

    기준이 있어야 나누지!! 어떤 기준으로 나눌지를 정의하는 단계이다.
    예를 들어 나는 SEQUENCE 를 기준으로 나누기를 원한다라고 생각 했다면,

    CREATE PARTITION FUNCTION MYRANGEPF1 (INT)
    AS RANGE LEFT FOR VALUES (1, 100, 1000);


    COL1 <= 1
    COL1 > 1 AND COL1 <= 100
    COL1 > 100 AND COL1 <= 1000
    COL1 > 1000

    CREATE PARTITION FUNCTION MYRANGEPF2 (INT)
    AS RANGE RIGHT FOR VALUES (1, 100, 1000);


    COL1 < 1
    COL1 >= 1 AND COL1 < 100
    COL1 >= 100 AND COL1 < 1000
    COL1 >= 1000

    /*************************
    [질문] 내 맘데로 넣으면 넌 어떻게 할꺼야?
    **************************/

    -- 실행
    CREATE DATABASE PDB
    GO
    USE PDB
    GO

    CREATE PARTITION FUNCTION MYRANGEPF3 (INT)
    AS RANGE LEFT FOR VALUES ( 1, NULL,1000, 100,-100);
    GO

    -- DROP PARTITION FUNCTION MYRANGEPF3

    결과

    경고: 파티션 함수 'MYRANGEPF3'의 범위 값 목록이 값을 기준으로 정렬되어 있지 않습니다. CREATE PARTITION SCHEME 작업 동안 파일 그룹에 파티션을 매핑하면 함수 'MYRANGEPF3'이(가) CREATE PARTITION SCHEME에서 참조된 경우 정렬된 경계 값을 사용합니다.

    -- 확인
    SELECT *
    FROM SYS.PARTITION_RANGE_VALUES
    WHERE
              FUNCTION_ID IN
                       (
                       SELECT FUNCTION_ID
                       FROM SYS.PARTITION_FUNCTIONS
                       WHERE NAME = ('MYRANGEPF3')
                       )

    결과

    FUNCTION_ID BOUNDARY_ID PARAMETER_ID VALUE
    ----------- ----------- ----------- -----------
    65536 1 1 NULL
    65536 2 1 -100
    65536 3 1 1
    65536 4 1 100
    65536 5 1 1000

    똑똑한 SQL 서버는 이 값들을 정렬해서 순서를 재배열 하여 사용하게 된다.
    또한 NULL 은 우선순위 최상으로 소팅되는 되는 것을 알수 있다.

    음 나누다 보니 보기 이상한데 하나더 나누어 보고 싶다면 어떻게 할까? 바꾸어 보자!

    /* 구문정보
    ALTER PARTITION FUNCTION PARTITION_FUNCTION_NAME()
    {
        SPLIT RANGE ( BOUNDARY_VALUE )
       | MERGE RANGE ( BOUNDARY_VALUE )
    } [ ; ]
    */

    생성된 모든 파티션을 확인 부터 해보자.
    개별 파티션 함수에 대한 정보를 얻으려면

    SELECT * FROM SYS.PARTITION_FUNCTIONS             --(파티션 함수정보)
    SELECT * FROM SYS.PARTITION_PARAMETERS               --(매개 변수 정보)
    SELECT * FROM SYS.PARTITION_RANGE_VALUES             --(경계구성)
    SELECT * FROM SYS.PARTITION_SCHEMES               --(파티션 구성정보)
    SELECT * FROM SYS.DATA_SPACES               --(파티션 구성정보)
    SELECT * FROM SYS.DESTINATION_DATA_SPACES              --(개별 파티션 구성정보)
    SELECT * FROM SYS.PARTITIONS             --(파티션 정보)
    SELECT * FROM SYS.TABLES              --(테이블 분할정보)
    SELECT * FROM SYS.INDEXES              --(인덱스 분할정보)
    SELECT * FROM SYS.INDEX_COLUMNS               --(인덱스 컬럼정보)

    -- SPLIT

    ALTER PARTITION FUNCTION MYRANGEPF3 ()
    SPLIT RANGE (-1000);
    GO

    SELECT *
    FROM SYS.PARTITION_RANGE_VALUES
    WHERE
             FUNCTION_ID IN
                     (
                     SELECT FUNCTION_ID
                     FROM SYS.PARTITION_FUNCTIONS
                     WHERE NAME = ('MYRANGEPF3')
                     )

    결과

    FUNCTION_ID BOUNDARY_ID PARAMETER_ID VALUE
    ----------- ----------- ----------- -----------
    65536 1 1 NULL
    65536 2 1 -1000
    65536 3 1 -100
    65536 4 1 1
    65536 5 1 100
    65536 6 1 1000

    알아서 잘 배열 되는 것을 관찰할 수 있다.

    -- MERGE
    ALTER PARTITION FUNCTION MYRANGEPF3 ()
    MERGE RANGE (-1000);

    FUNCTION_ID BOUNDARY_ID PARAMETER_ID VALUE
    ----------- ----------- ----------- -----------
    65536 1 1 NULL
    65536 2 1 -100
    65536 3 1 1
    65536 4 1 100
    65536 5 1 1000

    정리

    PARTITION_RANGE_VALUES + 1 개의 영역이 생기게 되고,기준값은 LEFT, RIGHT 의 지정에 따라 기준값의 위치가 다르게 속하게 되는 것을 알 수 있다. 예를들어 RIGHT 파티션을 만들고 경계값이 NULL 이 있다면, RIGHT 파티션의 경우 기준값을 포함하지 않는 더작은 영역을 만들게 된다. 그러나 NULL 보다 더 작은 값이 없으므로 첫번째 파티션은 비게 된다.

    DROP PARTITION FUNCTION MYRANGEPF3
    GO

    CREATE PARTITION FUNCTION MYRANGEPF3 (INT)
    AS RANGE RIGHT FOR VALUES ( NULL, -100, 1, 100, 1000);
    GO

    참고 : 기준값을 정열해서 배치하면 에러 없음
    5개로 기준값으로 나누었으니 다음과 같은 6개의 영역이 생긴다.

    1                                     값       <     NULL    --                                   첫번째 기준값 보다 작은 값들 (빈파티션)
    2    NULL      <=       값      <       -100       -- NULL                포함 기준값보다 더 큰 값들
    3    -100       <=       값      <        1                         -- -100                   포함 다음기준값 보다 작은 값들
    4    1                      <=     값       <           100                     -- 1                    포함 다음기준값 보다 작은 값들
    5    100                   <=     값       <           1000        -- 100                    포함 다음기준값 보다 작은 값들
    6                                     값       >=         1000        -- 1000                  포함 더 큰값들 다

    /*-----------------------------------------------------------------------
    PARTITIONED TABLE
              PARTITIONED FUNCTION
              PARTITIONED SCHEME
              CREATE PARTITIONED TABLE
              CHECK PARTITIONED INFORMATION
              PARTITION MERGE
              PARTITION SPLIT
              PARTITION SWITCH

              * PARTITIONED SCHEME
    파일그룹 매핑 구성표 만들기 (필수 선행작업: 파티션 함수 생성)
    -------------------------------------------------------------------------*/

    /* 구문정보
    CREATE PARTITION SCHEME partition_scheme_name
    AS PARTITION partition_function_name
    [ ALL ] TO ( { file_group_name | [ PRIMARY ] } [ ,...n ] )
    [ ; ]
    */

    ALL 을 지정하면 하나의 파일그룹만을 지정 할 수 있고, 파티션 펑션의 기준값 보다 같거나 많은 개수를 정의해야 하며, 적을 경우 에러를 나타내며 종료 많은경우 NEXT USED 로 정의 됨

    -- 실험
    CREATE PARTITION SCHEME PSMYSCHEME3
    AS PARTITION MYRANGEPF3
    TO (FG1,FG2)

    결과

    메시지 7707, 수준 16, 상태 1, 줄 2
    연결된 파티션 함수 'MYRANGEPF3'은(는) 구성표 'PSMYSCHEME3'에 언급된 파일 그룹보다 많은 파티션을 생성합니다.
    음~ 파일그룹이 있는지 없는지 검사하지도 않고 바로 에러!

    -- 설정하려는 파티션 펑션의 기준값 개수 확인
    SELECT *
    FROM SYS.PARTITION_RANGE_VALUES
    WHERE
              FUNCTION_ID IN
                         (
                         SELECT FUNCTION_ID
                         FROM SYS.PARTITION_FUNCTIONS
                         WHERE NAME = ('MYRANGEPF3')
                         )

    65537 1 1 NULL
    65537 2 1 -100
    65537 3 1 1
    65537 4 1 100
    65537 5 1 1000

    -- 음~ 최소 6개 만들고 더 만들면 다음파티션으로 쓰겠군...
    CREATE PARTITION SCHEME PSMYSCHEME3
    AS PARTITION MYRANGEPF3
    TO (FG1,FG2,FG3,FG4,FG5,FG6,FG7)

    결과

    메시지 208, 수준 16, 상태 58, 줄 1
    개체 이름 'FG1'이(가) 잘못되었습니다.
    개수를 만족하고 나서 파일 그룹이 실제로 있는지 찾음 해당데이터 베이스에 파일그룹이 없으면 에러가 난다.

    여기서 잠깐! 스토리지 이야기도 조금 하고 파일그룹 및 데이터 파일 핸들링 하는 방법에 대해서도 조금만 알아보자.

    데이터 베이스에서 파일의 숫자 만큼 쓰래드가 생성 될 수 있기 때문에 파일을 여러개 만드는 것이 좋다. 클러스터 인덱스가 있는 데이터 파일을 파일그룹, 난클러스터 인덱스는 다른 디스크에 위치시키는 것이 절대적으로 유리하다. 파일그룹을 잘게 나누면 파일그룹별로 백업 복구도 가능하고 용량산정을 위해 다른 위치로 옮길때도 옮기기 좋은 단위가 된다는 것을 상기하자!

    또한 로그파일[LDF] 는 분리된 다른 위치에 독립적으로 쓰게 하는것이 좋다. I/O 패턴이 다른 파일을 같은 디스크에 배치하는 것은 어떤경우에도 좋은 방법이 되지 못한다.

    개인적으로 로그는 300G 10000RPM RAID 1 을 추천하고 싶다. 최대 테이블 사이즈가 300기가를 넘는다면 다른 방법을 구상하도록 한다.

    또한 데이터 디스크는 RAID 1+0 를 추천한다. MMORPG 같은 경우나 빈번하고 짧은 트랜젝션이 많은 데이터베이스에서 쓰기스피드가 느린 RAID 5 는 CheckPoint시 트랜잭션 숫자를 감소시키는 주요 범인이 된다.

    로컬백업을 한다면 로컬백업이 되는 위치는 백업시간을 단축하기 위해 RAID 0 에 위치시킨다. 두장의 RAID 5 로 구성된 디스크 두장에 백업 하는것 보다 두장의 RAID 0 로 백업한다면 훨씬 더 빠른 백업을 수행 할 수 있다. 그러나 디스크 장애로 잦은 RAID 리빌딩을 하기 싫다면 디스크 에러에 강한 RAID 1+0 로 구성하는 것이 바르다고 할 수 있다.

    이런 디스크 문제가 빈번하게 일어난다면 스토리지 시스템을 도입하는 것을 고려해 보기 바란다. 스토리지 시스템은 자체 리드 캐쉬 라이트 캐쉬가 있기 때문에 메모리까지만 받아주면 스토리지 컨트롤러가 알아서 디스크에 쓸것이다. 최신 엔터프라이즈 스토리지는 컨트롤러 캐쉬가 16G에 이른다.

    -- 파일그룹 생성방법
    ALTER DATABASE PDB
    ADD FILEGROUP FG1

    -- 파일그룹에 간단한 파일 추가
    ALTER DATABASE PDB
    ADD FILE ( NAME = PDB_DAT1, FILENAME = 'C:\FG1\PDB_DAT1.ndf') TO FILEGROUP FG1

    -- 삭제방법
    -- 1, 파일 삭제
    ALTER DATABASE PDB
    REMOVE FILE PDB_DAT1

    -- 2, 파일그룹 삭제
    ALTER DATABASE PDB
    REMOVE FILEGROUP FG1

    -- 좀더 자세하게
    USE master
    GO
    ALTER DATABASE PDB
    ADD FILEGROUP FG1;
    ALTER DATABASE PDB
    ADD FILEGROUP FG2;
    ALTER DATABASE PDB
    ADD FILEGROUP FG3;
    ALTER DATABASE PDB
    ADD FILEGROUP FG4;
    ALTER DATABASE PDB
    ADD FILEGROUP FG5;
    ALTER DATABASE PDB
    ADD FILEGROUP FG6;
    ALTER DATABASE PDB
    ADD FILEGROUP FG7;
    ALTER DATABASE PDB
    ADD FILEGROUP FG8;
    GO

    -- 해당경로를 미리 만들어 두어야 한다.
    -- C:\ 드라이브에 FG1,FG2,FG3,FG4,FG5
    -- G:\ 드라이브에 FG6,FG7,FG8
    ALTER DATABASE PDB
    ADD FILE
    (
        NAME = PDB_DAT1,
        FILENAME = 'C:\FG1\PDB_DAT1.ndf', -- 테스트 이므로
        SIZE = 5MB,
        MAXSIZE = 100MB,
        FILEGROWTH = 5MB
    )
    --, -- 여러개의 파일로 분할 하고 싶은 경우
    --(
    -- NAME = PDB_DAT2,
    -- FILENAME = 'C:\FG1\PDB_DAT2.ndf',
    -- SIZE = 5MB,
    -- MAXSIZE = 100MB,
    -- FILEGROWTH = 5MB
    --)
    TO FILEGROUP FG1

    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT2,FILENAME = 'C:\FG2\PDB_DAT2.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG2
    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT3,FILENAME = 'C:\FG3\PDB_DAT3.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG3
    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT4,FILENAME = 'C:\FG4\PDB_DAT4.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG4
    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT5,FILENAME = 'C:\FG5\PDB_DAT5.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG5
    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT6,FILENAME = 'G:\FG6\PDB_DAT6.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG6
    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT7,FILENAME = 'G:\FG7\PDB_DAT7.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG7
    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT8,FILENAME = 'G:\FG8\PDB_DAT8.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG8

    -- 다시 하던일 계속하자!
    USE PDB
    GO

    CREATE PARTITION SCHEME PSMYSCHEME3
    AS PARTITION MYRANGEPF3
    TO (FG1,FG2,FG3,FG4,FG5,FG6,FG7,FG8)
    GO

    결과 R1

    파티션 구성표 'PSMYSCHEME3'이(가) 작성되었습니다. 파티션 구성표 'PSMYSCHEME3'에서 'FG7'은(는) 다음에 사용되는 파일 그룹으로 표시됩니다.
    다음에 사용되는 파일 그룹 다음에 지정된 1 파일 그룹은 무시됩니다.

    음~ 우리는 6개의 파일그룹만 필요한데, 더 만들어 두니 바로 다음 파일그룹은 NEXT USED 로 지정되고 나머지들은 무시되는 것이구나.

    -- 삭제방법
    -- DROP PARTITION SCHEME PSMYSCHEME3

    -- 해당디비의 상황
    EXEC SP_HELPDB N'PDB'
    EXEC SP_HELPFILEGROUP;
    EXEC SP_HELPFILE;
    SELECT * FROM SYS.SYSALTFILES WHERE DBID = DB_ID('PDB')

    여기서 잠깐!
    데이터 베이스 미러링 같은 경우 피지컬 영역 구성이 다를수 있고 스토리지 용량도 보장 할 수 없고 이런 파티션 스킴을 고치는 작업은 미러링중에 미러 대상서버에 적용되지 않는다.

    /*-----------------------------------------------------------------------
    PARTITIONED TABLE
            PARTITIONED FUNCTION
            PARTITIONED SCHEME
            CREATE PARTITIONED TABLE
            CHECK PARTITIONED INFORMATION
            PARTITION MERGE
            PARTITION SPLIT
            PARTITION SWITCH

            * CREATE PARTITIONED TABLE
    준비하는데 수고했습니다. 이제 파티션드 테이블을 만들어 보겠습니다.
    -------------------------------------------------------------------------*/

    -- 두번째 작업에서 힘들게 만들었던 파일스키마 정의에 테이블을 매핑 시켜 만든다.
    IF OBJECT_ID('DBO.PT_LOG') IS NOT NULL
    DROP TABLE DBO.PT_LOG
    GO

    CREATE TABLE [dbo].[PT_LOG] (
                 PIDX INT -- 파티션 기준값
            , IDX INT IDENTITY(1,1)
            , DATA VARCHAR(6000) NULL
    ) ON PSMYSCHEME3 (PIDX)
    GO

    /*-----------------------------------------------------------------------
    PARTITIONED TABLE
            PARTITIONED FUNCTION
            PARTITIONED SCHEME
            CREATE PARTITIONED TABLE
            CHECK PARTITIONED INFORMATION
            PARTITION MERGE
            PARTITION SPLIT
            PARTITION SWITCH

            * CHECK PARTITIONED INFORMATION ,PARTITION MERGE, PARTITION SPLIT
    데이터 삽입과 파티션 정보확인 및 MERGE SPLIT 해보도록 하겠습니다. NULL 은 어디에 있을까? 궁금하지 않나요?
    -------------------------------------------------------------------------*/

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (NULL, REPLICATE('A',6000))
    GO 601 -- 인서트 601번 실행

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (-100, REPLICATE('A',6000))
    GO 602

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (1, REPLICATE('A',6000))
    GO 603

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (100, REPLICATE('A',6000))
    GO 604

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (1000, REPLICATE('A',6000))
    GO 605

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (2000, REPLICATE('A',6000))
    GO 606

    SET STATISTICS IO ON
    SET STATISTICS PROFILE ON
    SELECT COUNT(*) FROM PT_LOG

    결과

       |--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(int,[Expr1008],0)))
             |--Stream Aggregate(DEFINE:([Expr1008]=Count(*)))
                    |--Nested Loops(Inner Join, OUTER REFERENCES:([PtnIds1007]) PARTITION ID:([PtnIds1007]))
    **                    |--Constant Scan(VALUES:(((1)),((2)),((3)),((4)),((5)),((6))))
                           |--Table Scan(OBJECT:([PDB].[dbo].[PT_LOG]))

    테이블 'PT_LOG'. 검색 수 6, 논리적 읽기 수 3621, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

    별표 별표(**) 부분이 어떤 파티션을 읽었는지 보여주눈 부분이다. 상수 조인을 이용한 6회 읽은 것을 알 수 있다.

    SET STATISTICS IO OFF
    SET STATISTICS PROFILE OFF

    결과

    3621

    SET STATISTICS IO ON
    SET STATISTICS PROFILE ON
    SELECT COUNT(*) FROM PT_LOG WHERE PIDX IS NULL
    SET STATISTICS IO OFF
    SET STATISTICS PROFILE OFF

    결과

       |--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(int,[Expr1010],0)))
             |--Stream Aggregate(DEFINE:([Expr1010]=Count(*)))
                   |--Table Scan(OBJECT:([PDB].[dbo].[PT_LOG]), WHERE:([PDB].[dbo].[PT_LOG].[PIDX] IS NULL) PARTITION ID:((2)))

    -- 파티션 아이디 2번에서만 읽은 것을 볼 수 있다. 아무리 봐도 넘 이쁜 넘이다.
    SET STATISTICS IO ON
    SET STATISTICS PROFILE ON

    SELECT COUNT(*)
    FROM PT_LOG
    WHERE
             PIDX IS NULL OR
             PIDX = 1
    GROUP BY PIDX
    HAVING PIDX = 1

    SET STATISTICS IO OFF
    SET STATISTICS PROFILE OFF

    결과

       |--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(int,[Expr1010],0)))
             |--Stream Aggregate(DEFINE:([Expr1010]=Count(*)))
                   |--Table Scan(OBJECT:([PDB].[dbo].[PT_LOG]), WHERE:([PDB].[dbo].[PT_LOG].[PIDX]=(1)) PARTITION ID:((4)))

    SELECT OBJECT_NAME(OBJECT_ID), *
    FROM SYS.PARTITIONS
    WHERE OBJECT_ID = OBJECT_ID('PT_LOG')
    ORDER BY PARTITION_NUMBER, INDEX_ID;

    결과

    partition_id   object_id      index_id      partition_number   hobt_id      rows

    PT_LOG 72057594040680448 53575229 0 1 72057594040680448 0
    PT_LOG 72057594040745984 53575229 0 2 72057594040745984 601
    PT_LOG 72057594040811520 53575229 0 3 72057594040811520 602
    PT_LOG 72057594040877056 53575229 0 4 72057594040877056 603
    PT_LOG 72057594040942592 53575229 0 5 72057594040942592 604
    PT_LOG 72057594041008128 53575229 0 6 72057594041008128 1211

    위와같이 RIGHT TYPE 에서는 NULL 보다 논리적으로 더 작은 것이 없으므로 제일 안쪽 파티션이 항상 비는 것을 알 수 있다.
    음~ 배운게 있으니 1211 을 적당하게 다시 나누어 보아야 겠다.
    파티션 기준값을 어떻게 나누었는지 다음 쿼리로 확인해 보자.

    SELECT *
    FROM SYS.PARTITION_RANGE_VALUES
    WHERE
         FUNCTION_ID IN
             (
             SELECT FUNCTION_ID
             FROM SYS.PARTITION_FUNCTIONS
             WHERE NAME = ('MYRANGEPF3')
             )

    결과

    function_id boundary_id parameter_id value
    ----------- ----------- ----------- -----
    65541 1 1 NULL
    65541 2 1 -100
    65541 3 1 1
    65541 4 1 100
    65541 5 1 1000

    2000으로 경계값을 더 나누어 보자 위에서 아래와 같은 데이터를 넣었다는것을 우리는 알고 있으므로

    -- INSERT INTO PT_LOG (PIDX, DATA) VALUES (2000, REPLICATE('A',6000))
    -- GO 606

    -- 다음과 같은 쿼리를 실행했다.
    ALTER PARTITION FUNCTION MYRANGEPF3 ()
    SPLIT RANGE (2000);
    GO

    결과 잘 나누어져 들어갔음을 확인 할 수 있다.

    FUNCTION_ID BOUNDARY_ID PARAMETER_ID VALUE
    ----------- ----------- ----------- -----
    65541 1 1 NULL
    65541 2 1 -100
    65541 3 1 1
    65541 4 1 100
    65541 5 1 1000
    65541 6 1 2000

    ALTER PARTITION FUNCTION MYRANGEPF3 ()
    SPLIT RANGE (3000);
    GO

    결과

    메시지 7707, 수준 16, 상태 1, 줄 1
    연결된 파티션 함수 'MYRANGEPF3'은(는) 구성표 'PSMYSCHEME3'에 언급된 파일 그룹보다 많은 파티션을 생성합니다.

    우리는 위에서 파티션 스키마를 만들때, 결과 R1 에서 NEXT USED 로 설정된 FG7 까지만 적용이 되고 FG8에는 무시되었던 것을 기억할 것입니다. 이런 결과 값이 나오는 테이블에서 2000 보다 큰 값이 들어갈 영역을 만들려고 했으나, 역시 무시되는 것을 알 수 있습니다. 그럼 NEXT USED로 다음 파일그룹이 사용될 영역만 다시 정의해 주면 될것을 생각할 수 있겠습니다.

    EXEC SP_HELP N'PT_LOG'

    결과

    Data_located_on_filegroup
    -------------------------
    PSMYSCHEME3

    -- 해당 테이블의 스키마 정보를 이용해서 어떤 파일그룹을 쓰고 있는지 조사
    SELECT
                A.NAME, A.DATA_SPACE_ID, A.IS_DEFAULT,
                B.DATA_SPACE_ID,
                C.NAME
    FROM
                SYS.DATA_SPACES A
                INNER JOIN SYS.DESTINATION_DATA_SPACES B
                        ON A.DATA_SPACE_ID = B.DATA_SPACE_ID
                INNER JOIN SYS.PARTITION_SCHEMES C
                        ON B.PARTITION_SCHEME_ID = C.DATA_SPACE_ID
    WHERE
                C.NAME = 'PSMYSCHEME3'

    결과

    NAME DATA_SPACE_ID IS_DEFAULT DATA_SPACE_ID NAME
    ---- ------------- ------------- ------------- ----
    FG1 2 0 2 PSMYSCHEME3
    FG2 3 0 3 PSMYSCHEME3
    FG3 4 0 4 PSMYSCHEME3
    FG4 5 0 5 PSMYSCHEME3
    FG5 6 0 6 PSMYSCHEME3
    FG6 7 0 7 PSMYSCHEME3
    FG7 8 0 8 PSMYSCHEME3

    -- 해당 디비의 파일그룹 조회
    EXEC SP_HELPFILEGROUP

    결과

    groupname groupid filecount
    --------- ------- ---------
    PRIMARY 1 1
    FG1 2 1
    FG2 3 1
    FG3 4 1
    FG4 5 1
    FG5 6 1
    FG6 7 1
    FG7 8 1
    FG8 9 1

    -- 해당 스킴의 파티션펑션 및 TYPE 정보 조회
    SELECT
                A.BOUNDARY_ID, A.VALUE,
                B.NAME,B.TYPE_DESC,B.FANOUT, BOUNDARY_VALUE_ON_RIGHT
    FROM SYS.PARTITION_RANGE_VALUES A
                INNER JOIN SYS.PARTITION_FUNCTIONS B
                ON A.FUNCTION_ID = B.FUNCTION_ID
                INNER JOIN SYS.PARTITION_SCHEMES C
                ON B.FUNCTION_ID = C.FUNCTION_ID
    WHERE
                C.NAME = 'PSMYSCHEME3'

    결과

    BOUNDARY_ID VALUE NAME TYPE_DESC FANOUT BOUNDARY_VALUE_ON_RIGHT
    ----------- ---------- --------- ------ -----------------------
    1 NULL MYRANGEPF3 RANGE 8 1
    2 -100 MYRANGEPF3 RANGE 8 1
    3 1 MYRANGEPF3 RANGE 8 1
    4 100 MYRANGEPF3 RANGE 8 1
    5 1000 MYRANGEPF3 RANGE 8 1
    6 2000 MYRANGEPF3 RANGE 8 1

    해당하는 스키마는 PRIMARY 파일 그룹과 FG8을 안쓰고 있다는 것을 알 수 있다.
    우리는 FG8을 다음 파티션 그룹으로 쓰기로 하자.

    -- 용법
    ALTER PARTITION SCHEME partition_scheme_name
    NEXT USED [ filegroup_name ] [ ; ]

    -- 실행
    ALTER PARTITION SCHEME PSMYSCHEME3
    NEXT USED FG8
    GO

    결과

    명령이 완료되었습니다.

    -- 확인
    SELECT
                 A.NAME, A.DATA_SPACE_ID, A.IS_DEFAULT,
                 B.DATA_SPACE_ID,
                 C.NAME
    FROM
                 SYS.DATA_SPACES A
                 INNER JOIN SYS.DESTINATION_DATA_SPACES B
                            ON A.DATA_SPACE_ID = B.DATA_SPACE_ID
                 INNER JOIN SYS.PARTITION_SCHEMES C
                            ON B.PARTITION_SCHEME_ID = C.DATA_SPACE_ID
    WHERE
                C.NAME = 'PSMYSCHEME3'

    groupname groupid filecount
    --------- ------- ---------

    BOUNDARY_ID VALUE NAME TYPE_DESC FANOUT BOUNDARY_VALUE_ON_RIGHT
    FG1 2 0 2 PSMYSCHEME3
    FG2 3 0 3 PSMYSCHEME3
    FG3 4 0 4 PSMYSCHEME3
    FG4 5 0 5 PSMYSCHEME3
    FG5 6 0 6 PSMYSCHEME3
    FG6 7 0 7 PSMYSCHEME3
    FG7 8 0 8 PSMYSCHEME3
    FG8 9 0 9 PSMYSCHEME3

    잘 적용된것을 확인 할 수 있다.

    -- 펑션을 수정해 보도록 하자 !
    ALTER PARTITION FUNCTION MYRANGEPF3 ()
    SPLIT RANGE (3000);
    GO

    잘 수행되는 것을 확인 할 수 있을 것이다.
    이제는 다양한 값을 넣어 보겠습니다.

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (NULL, REPLICATE('A',6000))
    GO 3

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (-99, REPLICATE('A',6000))
    GO 3

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (2, REPLICATE('A',6000))
    GO 3

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (101, REPLICATE('A',6000))
    GO 3

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (1001, REPLICATE('A',6000))
    GO 3

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (2001, REPLICATE('A',6000))
    GO 3

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (3000, REPLICATE('A',6000))
    GO 3

    INSERT INTO PT_LOG (PIDX, DATA)
    VALUES (3001, REPLICATE('A',6000))
    GO 3

    SELECT OBJECT_NAME(OBJECT_ID), *
    FROM SYS.PARTITIONS
    WHERE OBJECT_ID = OBJECT_ID('PT_LOG')
    ORDER BY PARTITION_NUMBER, INDEX_ID;

    결과

    groupname groupid filecount
    --------- ------- ---------

    PT_LOG 72057594041663488 117575457 0 1 72057594041663488 0
    PT_LOG 72057594041729024 117575457 0 2 72057594041729024 604
    PT_LOG 72057594041794560 117575457 0 3 72057594041794560 605
    PT_LOG 72057594041860096 117575457 0 4 72057594041860096 606
    PT_LOG 72057594041925632 117575457 0 5 72057594041925632 607
    PT_LOG 72057594041991168 117575457 0 6 72057594041991168 608
    PT_LOG 72057594042056704 117575457 0 7 72057594042056704 609
    PT_LOG 72057594042122240 117575457 0 8 72057594042122240 6

    SELECT
                $PARTITION.MYRANGEPF3(PIDX) AS 'PARTITION ID',
                MIN(PIDX) AS 'MIN',
                MAX(PIDX) AS 'MAX',
                COUNT(*) AS 'ROWCNT'
    FROM
                PT_LOG
    GROUP BY $PARTITION.MYRANGEPF3(PIDX)
    ORDER BY 1

    결과

    PARTITION ID MIN MAX ROWCNT
    ------------ ----------- ----------- -----------
    2 NULL NULL 604
    3 -100 -99 605
    4 1 2 606
    5 100 101 607
    6 1000 1001 608
    7 2000 2001 608
    8 3000 3001 6

    -- 파티션 머지
    ALTER PARTITION FUNCTION MYRANGEPF3 ()
    MERGE RANGE (1000);

    SELECT
                A.NAME, A.DATA_SPACE_ID, A.IS_DEFAULT,
                B.DATA_SPACE_ID,
                C.NAME
    FROM
                SYS.DATA_SPACES A
                INNER JOIN SYS.DESTINATION_DATA_SPACES B
                            ON A.DATA_SPACE_ID = B.DATA_SPACE_ID
                INNER JOIN SYS.PARTITION_SCHEMES C
                            ON B.PARTITION_SCHEME_ID = C.DATA_SPACE_ID
    WHERE
                C.NAME = 'PSMYSCHEME3'

    결과 FG6 이 없어진 것을 확인 할 수 있다.

    FG1 2 0 2 PSMYSCHEME3
    FG2 3 0 3 PSMYSCHEME3
    FG3 4 0 4 PSMYSCHEME3
    FG4 5 0 5 PSMYSCHEME3
    FG5 6 0 6 PSMYSCHEME3
    FG6 7 0 7 PSMYSCHEME3
    FG7 8 0 8 PSMYSCHEME3
    FG8 9 0 9 PSMYSCHEME3

    EXEC SP_HELPFILEGROUP

    결과

    PRIMARY 1 1
    FG1 2 1
    FG2 3 1
    FG3 4 1
    FG4 5 1
    FG5 6 1
    FG6 7 1
    FG7 8 1
    FG8 9 1

    물리적 파일 그룹은 그대로 이다.

    DBCC SHOWFILESTATS

    결과

    1 1 35 22 PDB C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\DATA\PDB.mdf
    3 2 80 1 PDB_DAT1 C:\FG1\PDB_DAT1.ndf
    4 3 80 78 PDB_DAT2 C:\FG2\PDB_DAT2.ndf
    5 4 80 78 PDB_DAT3 C:\FG3\PDB_DAT3.ndf
    6 5 80 78 PDB_DAT4 C:\FG4\PDB_DAT4.ndf
    7 6 160 78 PDB_DAT5 C:\FG5\PDB_DAT5.ndf
    8 7 160 78 PDB_DAT6 G:\FG6\PDB_DAT6.ndf
    9 8 80 79 PDB_DAT7 G:\FG7\PDB_DAT7.ndf
    10 9 80 2 PDB_DAT8 G:\FG8\PDB_DAT8.ndf

    정확하게 예전과 같이 분할된 것을 볼 수 있다.

    /*-----------------------------------------------------------------------
    PARTITIONED TABLE
                PARTITIONED FUNCTION
                PARTITIONED SCHEME
                CREATE PARTITIONED TABLE
                CHECK PARTITIONED INFORMATION
                PARTITION MERGE
                PARTITION SPLIT
                PARTITION SWITCH

                * PARTITION SWITCH
    이제 파티션 이동하기를 해보겠습니다. 대용량 로그 파일이 있을때 DELETE 로 지우면서
    세월아 내월아 기다릴 수 없으니 한방에 날릴 수 있도록 다른 파티션으로 이동해 보도록 하겠습니다.
    그리고 스키마 정보만 바꾸어서
    -------------------------------------------------------------------------*/
    EXEC SP_HELPFILEGROUP
    GO

    ALTER DATABASE PDB
    ADD FILEGROUP FG3_AUX;
    GO

    -- 폴더를 미리 만들고
    ALTER DATABASE PDB ADD FILE (NAME = PDB_DAT3_AUX,FILENAME = 'C:\FG3_AUX\PDB_DAT3_AUX.ndf',SIZE = 5MB,MAXSIZE = 100MB,FILEGROWTH = 5MB) TO FILEGROUP FG3_AUX

    IF OBJECT_ID('DBO.PT_LOG_AUX') IS NOT NULL
    DROP TABLE DBO.PT_LOG_AUX
    GO

    CREATE TABLE [dbo].[PT_LOG_AUX] (
                     PIDX INT -- 파티션 기준값
                 , IDX INT IDENTITY(1,1)
                 , DATA VARCHAR(6000) NULL
    ) ON FG3_AUX
    GO

    DBCC SHOWFILESTATS

    ALTER TABLE PT_LOG SWITCH PARTITION 3 TO [PT_LOG_AUX] ;
    GO

    메시지 4939, 수준 16, 상태 1, 줄 1
    ALTER TABLE SWITCH 문이 실패했습니다. 테이블 'PDB.dbo.PT_LOG_AUX'은(는) 파일 그룹 'FG3_AUX'에 있고 테이블 'PDB.dbo.PT_LOG'의 파티션 3은(는) 파일 그룹 'FG3'에 있습니다.

    -- 파일 그룹이 다른 테이블은 스위칭 하지 못한다.
    IF OBJECT_ID('DBO.PT_LOG_AUX') IS NOT NULL
    DROP TABLE DBO.PT_LOG_AUX
    GO

    CREATE TABLE [dbo].[PT_LOG_AUX] (
                     PIDX INT -- 파티션 기준값
                , IDX INT IDENTITY(1,1)
                , DATA VARCHAR(6000) NULL
    ) ON FG3
    GO

    ALTER TABLE PT_LOG SWITCH PARTITION 3 TO [PT_LOG_AUX] ;
    GO

    -- 성공적으로 스위칭 된다.

    SELECT * FROM PT_LOG WHERE PIDX = -100
    -- 하나도 없다.
    SELECT *
    FROM SYS.PARTITION_RANGE_VALUES
    WHERE
                FUNCTION_ID IN
                             (
                             SELECT FUNCTION_ID
                             FROM SYS.PARTITION_FUNCTIONS
                             WHERE NAME = ('MYRANGEPF3')
                             )

    SELECT * FROM PT_LOG_AUX
    -- 결과값이 나온다.

    DBCC SHOWFILESTATS
    -- 결국 같은 파일 그룹에서 정의만 다르게 해서 다른 테이블로 빼버리는 형태이다. 결론적으로 부하가 없다는 말입니다.

    ALTER TABLE PT_LOG_AUX SWITCH TO [PT_LOG] PARTITION 3 ;
    GO

    결과

    메시지 4982, 수준 16, 상태 1, 줄 2
    ALTER TABLE SWITCH 문이 실패했습니다. 원본 테이블 'PDB.dbo.PT_LOG_AUX'의 CHECK 제약 조건에서 대상 테이블 'PDB.dbo.PT_LOG'의 파티션 3에서 정의한 범위가 허용하지 않는 값을 허용합니다.

    SELECT OBJECT_NAME(OBJECT_ID), *
    FROM SYS.PARTITIONS
    WHERE OBJECT_ID = OBJECT_ID('PT_LOG')
    ORDER BY PARTITION_NUMBER, INDEX_ID;

    결과

    PT_LOG    72057594039042048    2073058421    0    3    72057594039042048    0
    실제 파티션이 비어 있음에도 FUNCTION 제약 조건이 단일 테이블에는 적용되어 있지 않기 때문에 다시 스위칭 하는것을 허용하지 않는것 같습니다.

    꽁수를 부려라...

    -- 꽁수 시작
    CREATE PARTITION FUNCTION MYRANGEPF3_AUX (INT)
    AS RANGE RIGHT FOR VALUES ( NULL, -100, 1, 100, 1000);
    GO

    CREATE PARTITION SCHEME PSMYSCHEME3_AUX
    AS PARTITION MYRANGEPF3
    ALL TO (FG3)

    IF OBJECT_ID('DBO.PT_LOG_SRC') IS NOT NULL
    DROP TABLE DBO.PT_LOG_AUX
    GO

    CREATE TABLE [dbo].[PT_LOG_SRC] (
                     PIDX INT -- 파티션 기준값
                 , IDX INT IDENTITY(1,1)
                 , DATA VARCHAR(6000) NULL
    ) ON PSMYSCHEME3_AUX(PIDX)
    GO

    SET IDENTITY_INSERT PT_LOG_SRC ON

    INSERT INTO PT_LOG_SRC (PIDX, IDX, DATA)
                 SELECT PIDX, IDX, DATA FROM PT_LOG_AUX

    SET IDENTITY_INSERT PT_LOG_SRC OFF

    ALTER TABLE PT_LOG_SRC SWITCH PARTITION 3 TO [PT_LOG] PARTITION 3 ;
    GO

    원래대로 끼워넣기 성공 파티션 스위치에 대한 감을 잡으셨기를 바랍니다.

    다음 기사는 MSDN 에 올라온 글입니다.
    관심있는 독자는 읽어보시길 바랍니다. SAN DISK 환경에서 일별로 어마어마한 로그가 쌓인다는 가정하에 2개의 자동화 프로시져를 이용하여 특정일 이후의 데이터를 삭제하는 멋진 로직을 설명 하고 있습니다. SQL2000 에서는 SP_RENAME 을 이용한 일별로그를 분리하곤 하지만 다양한 관리의 부담을 보이고 있으나, 오늘 배운 파티셔닝 기술과 SQL AGENT 로 두개의 프로시져만 번갈아 부르면 자동으로 현재일로부터 과거 몇일까지의 데이터를 저장하고 있는 테이블을 만들 수 있습니다.

    EXAMPLE
                HOW TO IMPLEMENT AN AUTOMATIC SLIDING WINDOW IN A PARTITIONED TABLE ON SQL 2005
                http://msdn2.microsoft.com/en-us/library/aa964122.aspx

    끝으로 이번에 장가 갑니다. 멀리서나마 축하해 주시면 감사하겠습니다.
    또한 김연욱 선배님(SQL MVP), 김대우님(MS DPE팀), 김종균님(TECHDATA), 송혁님(SQL MVP),장홍창님 그리고 많은 우리 SQL OFFLINE STUDY 사람들이 도와줘서 이글을 쓰게 되었습니다. 감사합니다.

    HTTP://SQLER.PE.KR에 오시면 석이를 만날 수 있습니다. ^^;
    부족한 글 끝까지 읽어 주셔서 감사합니다.

    원문 : http://blog.naver.com/backs55/100035813367

    + Recent posts