728x90

Microsoft SQL Server 2005를 위한 최상의 XML 실행 방법


저자: Shankar Pal, Vishesh Parikh, Vasili Zolotov, Leo Giakoumakis, Michael Rys Microsoft Corporation

2004년 4월

적용 대상:
Microsoft SQL Server

SQL Server 2005에서 XML 데이터 모델링 및 사용법에 대한 지침을 확인하고 자세한 예를 살펴봅니다. 이 문서를 최대한 활용하기 위해서는 SQL Server의 XML 기본 기능에 대해 이해해야 합니다. 기본 자료를 보려면 Microsoft Development Network에서 Microsoft SQL Server 2005의 XML 지원을 참조하십시오.


소개

SQL Server 2000 및 SQLXML 웹 릴리스는 강력한 XML 데이터 관리 기능을 제공합니다. 이러한 기능은 관계형 데이터 및 XML 데이터 간의 매핑에 중점을 둡니다. XML 데이터의 대량 로드, XML 데이터에 대한 쿼리 및 업데이트 기능을 지원하는 XML 중심 접근 방식을 제공하도록 주석 처리된 XSD(AXSD)를 사용하여 관계형 데이터에 대한 XML 뷰를 정의할 수 있습니다. Transact-SQL 확장은 FOR XML을 사용한 XML에 대한 관계형 쿼리 결과 매핑과 OpenXML을 사용한 XML로부터의 관계형 뷰 생성에 대한 SQL 중심 접근 방법을 제공합니다.

Microsoft SQL Server 2005는 XML 데이터 처리에 대한 포괄적인 지원을 제공합니다. XML 값을 XML 데이터 형식 열에 원래대로 저장하여 XML 스키마 컬렉션에 따라 형식이 있거나 형식이 없는 상태로 유지할 수 있습니다. XML 열을 인덱싱할 수 있습니다. 또한 XQuery 및 XML DML을 사용하여 세밀하게 조정된 데이터 조작이 지원되며 XML DML은 데이터 수정을 위한 확장으로 사용됩니다.

또한 SQL Server 2005에는 SQLXML, FOR XML 및 OpenXML 기능이 확장되었습니다. 네이티브 XML 지원이 새로 추가된 SQL Server 2005는 반구조적 및 구조화되지 않은 데이터 관리를 위한 다양한 기능의 응용 프로그램을 개발하기 위한 강력한 플랫폼을 제공합니다.

추가된 모든 기능을 통해 사용자는 자신의 데이터 저장소 및 응용 프로그램 개발에 맞는 다양한 디자인 요소를 활용할 수 있습니다. 이 문서에서는 SQL Server 2005에서 XML 데이터 모델링 및 사용법에 대한 지침을 제공합니다. 이 문서는 다음 두 항목으로 나눠져 있습니다.

① 데이터 모델링
SQL Server 2005에서 네이티브 XML 데이터 형식 및 테이블로 나눠진 XML 등의 여러 방식을 사용하여 XML 데이터를 저장할 수 있습니다. 이 항목에서는 XML 데이터 모델링에 적합한 항목을 선택하기 위한 지침을 제공합니다. 또한 XML 데이터 인덱싱, 속성 승격 및 XML 인스턴스 형식화에 대해 설명합니다.

② 사용법
이 항목에서는 XML 데이터를 서버로 로드하고, 쿼리 컴파일 중에 유형을 유추하는 등의 사용법과 관련된 항목에 대해 설명하고 관련 기능 및 기능 간 차이점에 대해 설명하고 이러한 기능의 적합한 사용법을 권장합니다. 이러한 개념들을 도표로 설명합니다.

데이터 모델링


이 섹션에서는 SQL Server 2005에서 XML을 사용하는 이유에 대해 간단히 설명하고 네이티브 XML 저장소 및 XML 뷰 기술 간의 선택 지침을 제공하고 데이터 모델링 권장 사항을 제공합니다.

관계형 데이터 또는 XML 데이터 모델

데이터가 알려진 스키마를 사용하여 상당히 세부적으로 구조화된 데이터 저장소의 경우 관계형 모델이 가장 적합할 수 있습니다. Microsoft SQL Server는 사용자에게 필요한 필수 기능 및 도구를 제공합니다. 반면에 구조가 유연한 경우(반구조적 또는 구조화되지 않은)나 알 수 없는 경우에는 이러한 데이터의 모델링에 더욱 많은 주의가 필요합니다.

구조적 및 의미적 태그를 사용하여 데이터의 이식성을 보장하기 위해 플랫폼에 의존하지 않는 모델이 필요한 경우에는 XML이 적합합니다. 또한 다음 속성 중 일부만 만족하는 경우에도 적합합니다.
  • 데이터가 산발적으로 되어 있고 데이터 구조를 모르거나 데이터 구조가 이후에 크게 바뀔 수 있습니다.
  • 데이터가 포함 계층(엔터티 간 참조와 반대)을 나타내고 재귀적일 수 있습니다.
  • 태그 및 값의 순서가 데이터에 내재적입니다.
  • 데이터 구조를 기준으로 데이터를 쿼리하거나 데이터 일부를 업데이트합니다.

이러한 조건 중 어느 것도 충족되지 않으면 관계형 데이터 모델을 사용해야 합니다. 예를 들어 데이터가 XML 형식이지만 응용 프로그램이 단순히 데이터베이스를 사용하여 데이터를 저장 및 검색하는 경우에는 [n]varchar(max) 열만 필요합니다.

데이터를 XML 열에 저장하면 엔진에서 데이터가 미리 지정된 XML 스키마에 따라 잘 형성되었거나 유효한지 확인할 수 있는 추가 장점이 있으며 XML 데이터에 대한 세밀하게 조정된 쿼리 및 업데이트가 지원됩니다.

SQL Server 2005에 XML 데이터를 저장하는 이유

다음은 파일 시스템에서 XML 데이터를 관리하는 것과는 반대로 SQL Server 2005에서 네이티브 XML 기능을 사용하는 일부 이유입니다.

  • 백업, 복구 및 복제와 같은 XML 데이터 관리를 위해 데이터베이스 서버의 관리 기능을 사용합니다.
  • XML 데이터를 효율적인 트랜잭션 방식으로 공유, 쿼리 및 수정합니다. 응용 프로그램에 세밀하게 조정된 데이터 액세스가 중요합니다. 예를 들어 XML 문서 내에서 일부 섹션을 추출하거나 전체 문서를 바꾸지 않고 새 섹션을 삽입합니다.
  • 관계형 데이터와 SQL 응용 프로그램이 있으며 응용 프로그램 내에서 관계형 데이터와 XML 데이터 간의 상호 운영이 필요합니다. 도메인 간 응용 프로그램에 대해 쿼리 및 데이터 수정을 위한 언어 지원이 필요합니다.
  • 서버에서 잘 형성된 데이터를 보장하고 선택적으로 XML 스키마에 따라 데이터의 유효성을 검사해야 합니다.
  • 효율적인 쿼리 처리 및 뛰어난 확장성을 위한 XML 데이터 인덱싱 및 최상의 쿼리 최적화 프로그램을 사용해야 합니다.
  • XML 데이터에 대한 SOAP, ADO.NET 및 OLE DB 액세스가 필요합니다.

이러한 조건 중 어느 것도 충족되지 않으면 데이터를 [n]varchar(max) 또는 varbinary(max)와 같은 비-XML의 큰 개체 유형으로 저장하는 것이 좋습니다.

XML 저장소 옵션

SQL Server 2005에서 XML에 대한 저장소 옵션은 다음과 같습니다.

  • XML 데이터 형식의 네이티브 저장소:
    포함 계층, 문서 순서, 요소 및 특성 값과 같은 데이터의 XML 내용이 보존되는 내부 표현으로 데이터가 저장됩니다. 특히 XML 데이터의 InfoSet 내용이 보존됩니다(InfoSet에 대한 자세한 내용은 http://www.w3.org/TR/xml-infoset (영문) 참조). 이러한 데이터는 중요하지 않은 공백, 특성 순서, 네임스페이스 접두사 및 XML 선언과 같은 정보가 유지되지 않기 때문에 텍스트 XML의 정확한 복사본이 아닐 수 있습니다.
    형식 있는 XML 데이터 형식(즉, XML 스키마에 바인딩된 XML 데이터 형식)의 경우 Infoset에 유형 정보를 추가하는 PSVI(스키마 이후 유효성 검사) Infoset의 유형 관련 정보는 내부 표현으로 인코딩됩니다. 이러한 방식은 구문 분석 속도를 크게 향상시킵니다. 자세한 내용은 http://www.w3.org/TR/xmlschema-1 (영문) 및 http://www.w3.org/TR/xmlschema-2 (영문) 의 W3C XML 스키마 사양과 http://www.w3.org/TR/2005/WD-xpath-datamodel-20050211 (영문)의 XQuery 1.0 및 XPath 2.0 데이터 모델 초안을 참조하십시오.

  • XML과 관계형 저장소 간의 매핑:
    주석 처리된 스키마(AXSD)를 사용할 때 XML은 하나 이상의 테이블에 있는 열로 분해되어 관계형 수준에서 데이터의 정확도가 보존됩니다. 이 때 계층 구조는 보존되고 요소 간 순서는 무시됩니다. 스키마는 재귀적일 수 없습니다.

  • 큰 개체 저장소([n]varchar(max) 및 varbinary(max)):
    데이터의 정확한 복사본이 저장됩니다. 이 방법은 법률 문서와 같은 특별한 목적의 응용 프로그램에 유용합니다. 대부분의 응용 프로그램에는 정확한 복사본이 필요하지 않으며 XML 내용으로도 충분합니다(Infoset 정확도).

    일반적으로 이러한 접근 방법을 조합해서 사용해야 할 수 있습니다. 예를 들어 XML 데이터는 XML 데이터 형식 열에 저장하고 속성을 관계형 열로 승격해야 할 수 있습니다. 또는 매핑 기술을 사용하고 비재귀적인 부분을 비-XML 열에 저장하고 다른 재귀적 부분은 XML 데이터 형식 열에 저장할 수 있습니다.

    XML 기술 선택

    XML 기술의 선택(네이티브 XML과 XML 뷰)은 일반적으로 다음과 같은 요소에 달려 있습니다.
  •   저장소 옵션:
    XML 데이터가 큰 개체 저장소(예: 제품 설명서)에 더 적합하거나 관계형 열의 저장소(예: XML로 변환된 라인 항목)에 더 적합할 수 있습니다. 각 저장소 옵션은 문서 정확도를 유지하는 정도가 다릅니다.
  • 쿼리 기능:
    쿼리 특성과 XML 데이터를 쿼리하는 정도에 따라 적합한 옵션이 달라질 수 있습니다. XML 노드에 대한 조건문 평가와 같은 XML 데이터에 대한 세밀하게 조정된 쿼리는 두 가지 옵션의 여러 수준에 맞게 지원됩니다.

  • XML 데이터 인덱싱
    XML 쿼리 성능을 높이기 위해 XML 데이터를 인덱싱할 수 있습니다. 인덱싱 옵션은 저장소 옵션에 따라 다르며 자신의 작업을 최적화할 수 있는 적합한 옵션을 선택해야 합니다.

  • 데이터 수정 기능:
    일부 작업에는 세밀하게 조정된 XML 데이터 수정 기능(예: 문서 내에 새 섹션 추가)이 필요하지만 다른 작업(예: 웹 콘텐츠)은 그렇지 않습니다. 이 경우 응용 프로그램에 대한 데이터 수정 언어 지원이 중요할 수 있습니다.

  • 스키마 지원:
    XML 스키마 문서인지 여부에 관계 없이 스키마로 XML 데이터를 기술할 수 있습니다. 스키마 바인딩된 XML 지원은 XML 기술에 따라 달라집니다.

    물론 각 선택에 따라 성능 특성이 달라집니다.

    네이티브 XML 저장소

    서버에서 XML 데이터 형식 열에 XML 데이터를 저장할 수 있습니다. 이러한 방법은 다음 경우에 적합합니다.

  • 문서 순서 및 문서 구조를 유지하면서 서버에서 XML 데이터를 직관적으로 저장할 수 있는 방법이 필요합니다.
  • XML 데이터에 대한 스키마가 있거나 없을 수도 있습니다.
  • XML 데이터를 쿼리하고 수정해야 합니다.
  • 신속한 쿼리 처리를 위해 XML 데이터를 인덱싱합니다.
  • 응용 프로그램에 XML 데이터 및 XML 스키마 관리를 위한 시스템 카탈로그 뷰가 필요합니다.

    네이티브 XML 저장소는 구조 범위가 다양한 XML 문서나 관계형 구조로 매핑하기 어려운 복잡하거나 서로 다른 스키마를 기반으로 하는 XML 문서가 있는 경우에 유용합니다.

    예제: XML 데이터 형식을 사용하여 XML 데이터 모델링

    각 항목에 대한 별개의 장이 있고 각 장 안에 여러 섹션이 포함된 XML 형식의 제품 설명서를 가정해 보십시오. 섹션에는 하위 섹션이 포함될 수 있으므로
    은 재귀적 요소입니다. 제품 설명서에는 다양한 혼합된 내용, 다이어그램, 기술 자료 등이 포함되며 데이터는 반구조화된 상태입니다. 사용자는 자신이 원하는 항목을 문맥에 따라 검색할 수 있으며(예: "인덱싱" 장 내에서 "클러스터형 인덱스"에 대한 섹션 검색) 기술 수량을 쿼리할 수 있습니다.

    이러한 XML 문서에 적합한 저장소 모델은 XML 데이터 형식 열입니다. 이러한 형식은 XML 데이터의 Infoset 콘텐츠를 유지합니다. XML 열을 인덱싱하면 쿼리 성능이 향상됩니다.

    예제: XML 데이터의 정확한 복사본 유지

    정부 규제에 따라 XML 문서(예: 서명된 문서, 법률 문서 또는 주식 거래 주문)에 대한 정확한 텍스트 복사본을 유지해야 한다고 가정해 보십시오. 이 경우 문서를 [n]varchar(max) 열에 저장할 수 있습니다.

    쿼리를 수행하기 위해서는 데이터를 런타임에 XML 데이터 형식으로 변환하고 이에 대해 XQuery를 실행합니다. 문서가 큰 경우 런타임 변환은 특히 비용이 많이 듭니다. 쿼리를 자주 수행하는 경우 문서를 XML 데이터 형식 열로 중복해서 저장하고 인덱싱하면서도 [n]varchar(max) 열로부터 정확한 문서 복사본을 반환할 수 있습니다.

    XML 열은 [n]varchar(max) 열을 기준으로 계산된 열일 수 있습니다. 하지만 XML 계산된 열에는 XML 인덱스를 만들 수 없으며 [n]varchar(max) 또는 varbinary(max) 열에도 XML 인덱스를 작성할 수 없습니다.

    XML 뷰 기술

    XML 스키마와 데이터베이스의 테이블 간 매핑을 정의하여 영구적 데이터에 대한 "XML 뷰"를 만듭니다. XML 대량 로드 작업에서는 XML 뷰를 사용하여 기본 테이블을 채울 수 있습니다. XPath 1.0을 사용하여 XML 뷰를 쿼리할 수 있습니다. 이 쿼리는 테이블에 대한 SQL 쿼리로 변환됩니다. 비슷하게 업데이트도 이러한 테이블에 전파됩니다.

    이러한 기술은 다음과 같은 경우에 유용합니다

  • 기존 관계형 데이터에 대해 XML 뷰를 사용하는 XML 중심 프로그래밍 모델이 필요합니다.
  • 외부 파트너가 제공한 XML 데이터에 대한 스키마(XSD, XDR)가 있습니다.
  • 데이터 순서가 중요하지 않고, 쿼리 가능한 데이터가 재귀적이지 않거나, 최대 재귀 수준이 미리 알려져 있습니다.
  • XPath 1.0을 사용한 XML 뷰를 통해 데이터를 쿼리하고 수정해야 합니다.
  • XML 데이터를 대량 로드하고 이를 XML 뷰를 사용하여 기본 테이블로 분해해야 합니다.

    이러한 예로는 데이터 교환 및 웹 서비스를 위해 XML로 제공된 관계형 데이터와 고정 스키마가 포함된 XML 데이터가 있습니다. 자세한 내용은 http://msdn.microsoft.com/SQLXML (영문)을 참조하십시오.

    또한 FOR XML을 사용하여 서버에 저장된 관계형 데이터 및 XML 데이터로부터 XML을 게시할 수 있습니다. 자세한 내용은 이 문서의 FOR XML을 사용하여 행 집합으로부터 XML 생성을 참조하십시오.

    예제: 주석 처리된 XML 스키마(AXSD)를 사용하여 데이터 모델링

    XML로 조작하길 원하는 기존 관계형 데이터(예: 고객, 주문 및 라인 항목)가 있다고 가정해 보십시오. 관계형 데이터에 대해 AXSD를 사용하여 XML 뷰를 정의합니다. 이 XML 뷰를 사용하면 XML 데이터를 테이블에 대량 로드하고 XML 뷰를 사용하여 관계형 데이터를 쿼리 및 업데이트할 수 있습니다. 이 모델은 SQL 응용 프로그램을 방해 받지 않고 작동하면서 다른 응용 프로그램과 XML 태그가 포함된 데이터를 교환해야 할 경우에 유용합니다.

    혼합 모델

    관계형 및 XML 데이터 형식 열의 조합은 데이터 모델링에 적합한 경우가 많습니다. XML 데이터의 일부 값은 관계형 열에 저장하고 남은 값이나 전체 XML 값은 XML 열에 저장할 수 있습니다. 이렇게 하면 관계형 열에 생성된 인덱스를 완전히 제어하는 등의 더 나은 성능과 잠금 특성을 얻을 수 있습니다. 하지만 데이터 저장소 관리에 대한 책임이 늘어납니다.

    관계형 열에 저장되는 값은 작업에 따라 달라집니다. 예를 들어 경로 식 /Customer/@CustId를 기준으로 전체 XML 값을 검색하는 경우 CustId 특성의 값을 관계형 열로 승격하고 이를 인덱싱하면 더욱 빠른 쿼리 성능을 얻을 수 있습니다. 하지만 XML 데이터가 포괄적이고 비중복적으로 관계형 열에 분산되어 있는 경우 재조합 비용이 매우 커질 수 있습니다.

    구조화 수준이 높은 XML 데이터(예: 테이블 내용이 XML로 변환되어 있는 경우)의 경우 XML 뷰 기술을 사용하여 모든 값을 관계형 열로 매핑할 수 있습니다.

    XML 데이터 형식을 사용하여 데이터 모델링

    이 섹션에서는 네이티브 XML 저장소에 대한 데이터 모델링 항목에 대해 다룹니다. 여기에는 XML 데이터 인덱싱, 속성 승격 및 형식 있는 XML 데이터 형식이 포함됩니다.

    동일 테이블 또는 다른 테이블

    XML 데이터 형식 열을 다른 관계형 열이 포함된 테이블이나 기본 테이블에 대한 외래 키 관계에 있는 별개의 테이블에 만들 수 있습니다.

    다음 조건 중 하나에 해당하는 경우 동일 테이블에 XML 데이터 형식 열을 만듭니다.

  • 응용 프로그램이 XML 열에서 데이터 검색을 수행하고 XML 열에 대한 XML 인덱스는 필요하지 않습니다.
  • XML 데이터 형식 열에 XML 인덱스를 작성해야 하고 기본 테이블의 기본 키가 해당 클러스터링 키와 동일합니다. 자세한 내용은 XML 데이터 형식 열 인덱싱의 섹션을 참조하십시오 다음 조건에 해당하는 경우 별개의 테이블에 XML 데이터 형식 열을 만듭니다.

  • XML 데이터 형식 열에 XML 인덱스를 작성해야 하지만 기본 테이블의 기본 키가 해당 클러스터링 키와 동일하지 않거나 기본 테이블에 기본 키가 없거나 기본 테이블이 힙(즉, 클러스터링 키가 없음)입니다. 이 경우는 기본 테이블이 이미 있는 경우에 해당할 수 있습니다.
  • 행 내부 또는 행 외부에 저장되는지 여부에 관계없이 공간을 차지하는 테이블에 있는 XML 열로 인해 테이블 스캔이 느려지지 않아야 합니다.

    XML 데이터의 세분성

    XML 열에 저장된 XML 데이터의 세분성은 잠금 및 업데이트 특성에 중요한 요소입니다. SQL Server는 XML 데이터 및 비-XML 데이터에 대해 같은 잠금 메커니즘을 사용합니다. 따라서 행 수준의 잠금을 사용하면 해당 행에 있는 모든 XML 인스턴스가 잠깁니다. 세분성이 큰 경우 큰 XML 인스턴스를 업데이트를 위해 잠글 경우 다중 사용자 시나리오에서 처리량이 줄어듭니다. 고급 업데이트 시나리오에서 동시성을 향상시키기 위해서는 XML 데이터를 하나 이상의 테이블에서 관계형 열로 분해할 수 있습니다. 이렇게 분산 수준이 높은 경우 개체 캡슐화 및 XML 데이터의 구조가 손실될 가능성이 있으며 재조합 비용이 높아집니다.

    XML 인스턴스에 대한 업데이트는 즉각적이고 증분적으로 수행됩니다. 즉, 일반적으로 전체 XML 인스턴스를 바꾸는 경우가 거의 없습니다. 따라서 단일 특성 값의 업데이트를 XML 인스턴스의 크기에 관계없이 효율적으로 처리할 수 있습니다.

    데이터 모델링 요구 사항 및 잠금 특성 간의 균형은 훌륭한 디자인을 위한 중요한 요소입니다.

    형식 없는, 형식 있는, 제한된 XML 데이터 형식

    SQL Server 2005 XML 데이터 형식은 ISO SQL-2003 표준 XML 데이터 형식을 구현합니다. 따라서 올바른 형식의 XML 1.0 문서 뿐만 아니라 텍스트 노드와 임의 개수의 형식 없는 XML 열의 최상위 요소가 포함된 XML 콘텐츠 조각도 저장할 수 있습니다. 시스템은 데이터에 대한 올바른 형식을 검사하며, 열이 XML 스키마에 바인딩될 필요가 없고, 넓은 의미에서 올바른 형식이 아닌 데이터는 거부합니다. 이러한 경우는 형식 없는 XML 변수 및 매개 변수에 대해서도 적용됩니다.

    XML 데이터를 기술하는 XML 스키마가 있는 경우 이 스키마를 XML 열과 연결하여 형식 있는 XML을 만들 수 있습니다. XML 스키마는 데이터 유효성을 검사하고, 형식 없는 XML 이외의 데이터 수정 문과 쿼리를 컴파일 하는 동안 보다 정확한 유형 검사를 수행하고, 저장소 및 쿼리 처리를 최적화하는 데 사용됩니다.

    다음 조건에 해당하는 경우 형식 없는 XML 데이터 형식을 사용합니다.

  • XML 데이터에 대한 스키마가 없습니다.
  • 스키마는 있지만 서버에서 데이터의 유효성을 검사할 필요가 없습니다. 이러한 경우는 응용 프로그램이 데이터를 서버에 저장하기 전에 클라이언트측 유효성 검사를 수행하거나, 스키마에 유효하지 않은 XML 데이터를 일시적으로 저장하거나, 서버에서 지원되지 않는 XML 스키마 기능(예:key/keyref)을 사용하는 경우에 해당합니다.

    다음 조건에 해당하는 경우 형식 있는 XML 데이터 형식을 사용합니다.

  • XML 데이터에 대한 스키마가 있고 서버에서 XML 스키마에 따라 XML 데이터의 유효성을 검사해야 합니다.
  • 유형 정보에 따라 저장소와 쿼리 최적화를 활용합니다.
  • 정적 유형 오류와 같이 쿼리 컴파일 중에 유형 정보를 활용합니다.

    형식 있는 XML 열, 매개 변수 및 변수는 선언 시에 플래그(각각 DOCUMENT 또는 CONTENT)로 지정해야 하는 XML 문서 또는 콘텐츠를 저장할 수 있습니다. 또한 하나 이상의 XML 스키마를 제공해야 합니다. 각 XML 인스턴스에 정확히 하나의 최상위 요소가 있는 경우 DOCUMENT를 지정하고 그렇지 않으면 CONTENT를 사용하십시오. 쿼리 컴파일러는 쿼리 컴파일 중에 유형 검사에 DOCUMENT 플래그를 사용하여 단일 최상위 요소를 유추합니다.

    XML 열 형식화 외에도 형식 있는 또는 형식 없는 XML 데이터 형식 열에 관계형(열 또는 행) 제약 조건을 사용할 수 있습니다. 다음 조건에 해당하는 경우 제약 조건을 사용합니다.

  • XML 스키마로 비즈니스 규칙을 표현할 수 없습니다. 예를 들어 꽃집의 위치로부터 80km 이내에 있어야 하는 꽃집의 배달지는 XML 열에 대한 제약 조건으로 작성될 수 있습니다. 이 제약 조건에는 스칼라(테이블 값에 반대됨) 사용자 정의 함수 내의 XML 데이터 형식 메서드가 포함됩니다.
  • 이러한 제약 조건에는 테이블에 있는 다른 XML 또는 비-XML 열이 포함됩니다. 이에 대한 예로는 XML 인스턴스에 있는 고객 ID(/Customer/@CustId)를 관계형 CustomerID 열의 값과 일치하도록 강제로 적용하는 경우를 들 수 있습니다.

    DTD(문서 유형 정의)

    XML 데이터 형식 열, 변수 및 매개 변수는 DTD가 아닌 XML 스키마를 사용하여 형식화할 수 있습니다. DTD는 타사 도구를 사용하여 XML 스키마 문서로 변환할 수 있으며 XML 스키마를 데이터베이스에 로드할 수 있습니다.

    형식 없는 XML 인스턴스와 형식 있는 XML 인스턴스에 대해 인라인 DTD를 사용하여 기본값을 제공하고 엔터티 참조를 해당 확장 형식으로 교체할 수 있습니다.

    XML 데이터의 내부 저장소

    사용자가 제공한 XML 데이터는 내부적으로 이진 형식으로 저장되며 XML 데이터의 텍스트 표현보다 신속하게 구문 분석할 수 있습니다. 이러한 이진 형식은 일반적인 경우 약간의 압축을 제공하며 인스턴스당 2GB로 제한됩니다. 압축의 정도는 반복되는 태그의 길이 및 수와 XML 데이터에 있는 값의 유형에 따라 달라집니다. 다음 예제에서는 저장된 XML 데이터의 크기를 계산하는 방법을 보여 줍니다.

    예제: 저장된 XML 크기 계산

    여기에서는 이 문서에 있는 대부분의 예제에 사용된 형식 없는 XML 열이 있는 테이블 문서(pk INT PRIMARY KEY, xCol XML)가 사용되며 이러한 문서는 직관적인 방식으로 형식화된 XML로 확장될 수 있습니다(형식화된 XML 사용에 대한 자세한 내용은 SQL Server 2005 온라인 설명서 참조).

    CREATE TABLE docs (pk INT PRIMARY KEY, xCol XML)

    이해를 돕기 위해 XML 데이터 인스턴스에 대한 쿼리가 다음과 같이 설명됩니다.

    INSERT INTO docs VALUES (1,
    ''


    Michael
    Howard


    David
    LeBlanc

    39.99
    '')

    XML 열에서 XML 인스턴스의 저장 크기(바이트)는 DATALENGTH() 함수를 사용하여 찾을 수 있습니다.

    SELECT DATALENGTH (xCol)
    FROM docs

    행 내부 및 행 외부 저장소

    크기가 작은 XML 데이터 형식 인스턴스는 테이블의 일부 행 내에 저장됩니다. 디스크 페이지 내에 수용할 수 없는 보다 큰 값은 16바이트의 행 내부 포인터와 함께 행 외부에 저장됩니다.

    행 내부에 XML 값을 저장하면 레코드 밀도가 줄어들고 테이블의 비-XML 열에 대한 테이블 검색 속도가 느려집니다. 이러한 경우 시스템 저장 프로시저 sp_tableoption에서 "large value types out of row" 옵션을 지정하여 모든 큰 데이터 유형을 행 외부에 저장할 수 있습니다.

    XML 데이터 형식 열 인덱싱

    XML 데이터 형식 열에서 XML 인덱스를 만들 수 있습니다. 열에 있는 XML 인스턴스에 대한 모든 태그, 값 및 경로가 인덱싱되며 쿼리 성능이 향상됩니다. 다음과 같은 경우 XML 인덱스를 사용하면 응용 프로그램에 도움이 됩니다.

  • 작업에서 XML 열에 대한 쿼리가 일반적으로 사용됩니다. 데이터 수정 중의 XML 인덱스 유지 관리 비용을 고려해야 합니다.
  • XML 값이 비교적 크고 검색된 부분은 비교적 작습니다. 인덱스를 만들면 런타임시 전체 데이터의 구문 분석을 방지하고 효율적인 쿼리 처리를 위해 인덱스 조회 기능을 활용할 수 있습니다.

    XML 열에서 첫 번째 인덱스는 "기본 XML 인덱스"입니다. 이를 사용하여 XML 열에서 세 가지 유형의 보조 XML 인덱스를 만들면 아래에 설명된 대로 일반적인 쿼리 속도를 향상시킬 수 있습니다.

    기본 XML 인덱스

    이 인덱스는 XML 열의 XML 인스턴스 내에 있는 모든 태그, 값 및 경로를 인덱싱합니다. 기준 테이블(즉, XML 열이 있는 테이블)에는 테이블의 기본 키에 클러스터형 인덱스가 있어야 하며 기본 키는 기준 테이블의 행과 인덱스 행의 상관 관계를 지정하는 데 사용됩니다. 전체 XML 인스턴스는 XML 열로부터 검색됩니다(예: SELECT *). 쿼리에는 기본 XML 인덱스가 사용되며, 이 인덱스를 사용하여 스칼라 값 또는 XML 하위 트리를 반환합니다.

    예제: 기본 XML 인덱스 만들기

    다음 문에서는 테이블 문서의 XML 열 xCol에 idx_xCol이라는 기본 XML 인덱스를 만듭니다.

    CREATE PRIMARY XML INDEX idx_xCol on docs (xCol)

    보조 XML 인덱스


    기본 XML 인덱스를 만든 다음에는 보조 XML 인덱스를 만들어서 작업 내 여러 종류의 쿼리 속도를 높일 수 있습니다. 보조 XML 인덱스의 세 가지 유형인 PATH, PROPERTY 및 VALUE를 사용하면 각각 경로 기반 쿼리, 사용자 지정 속성 관리 시나리오 및 값 기반 쿼리에 큰 이점으로 작용합니다.

    PATH 인덱스는 열의 모든 XML 인스턴스에 대해 문서 순서에 있는 각 XML 노드의 경로 및 값 쌍에 B+-트리를 작성합니다. PROPERTY 인덱스는 각 XML 인스턴스 내의 PK, 경로 및 값 쌍에 클러스터된 B+-트리를 만들고, 여기서 PK는 기준 테이블의 기본 키입니다. 마지막으로 VALUE 인덱스는 열의 모든 XML 인스턴스에 대해 문서 순서에 있는 각 노드의 경로 및 값 쌍에 B+-트리를 만듭니다.

    다음은 이러한 인덱스를 하나 이상 만들기 위한 일부 지침입니다.


  • 작업에서 주로 XML 열에 대한 경로 식이 사용되는 경우 PATH 보조 XML 인덱스를 사용하면 작업 속도가 높아질 수 있습니다. 가장 일반적인 경우는 Transact-SQL의 WHERE 절에 있는 XML 열의 exist() 메서드를 사용하는 것입니다.
  • 작업에서 경로 식을 사용하여 개별 XML 인스턴스로부터 여러 값을 검색하는 경우 PROPERTY 인덱스의 각 XML 인스턴스 내에서 경로를 클러스터하는 것이 도움이 될 수 있습니다. 이 시나리오는 일반적으로 개체의 속성이 인출되고 해당 관계형 기본 키 값이 알려진 속성 모음 시나리오에서 발생합니다.
  • 작업이 XML 인스턴스 내의 값에 대한 쿼리를 포함하고 이러한 값이 포함된 요소 또는 특성 이름을 알 수 없는 경우 VALUE 인덱스를 만들 수 있습니다. 이 경우는 일반적으로 요소는 계층의 임의 수준에서 발생할 수 있고 검색 값(“Howard”)은 경로보다 선택적인 //author[last-name="Howard"]와 같은 하위 항목 축 조회에서 발생합니다. 또한 쿼리에서 “novel”이라는 값이 포함된 일부 특성의 요소를 조회하는 /book [@* = "novel"]과 같은 “와일드카드” 쿼리에서 발생합니다.

    예제: 경로 기반 조회

    아래 쿼리가 작업에서 일반적으로 사용된다고 가정해 보십시오.

    SELECT pk, xCol
    FROM docs
    WHERE xCol.exist (''/book[@genre = "security"]'') = 1

    경로 식 /book/@genre와 값 "security"는 PATH 인덱스의 키 필드에 해당합니다. 따라서 PATH 유형의 보조 XML 인덱스는 이 작업에 도움이 됩니다.

    CREATE XML INDEX idx_xCol_Path on docs (xCol)
    USING XML INDEX idx_xCol FOR PATH

    예제: 개체의 속성 인출

    테이블 문서의 각 행에서 책 저자의 이름을 검색하는 다음 쿼리를 보십시오.

    SELECT ref.value (''first-name'', ''nvarchar(64)''),
    ref.value (''last-name'', ''nvarchar(64)'')
    FROM docs CROSS APPLY xCol.nodes (''/book/author) R(ref)

    이 경우 속성 인덱스가 유용하고 다음과 같이 생성됩니다.

    CREATE XML INDEX idx_xCol_Property on docs (xCol)
    USING XML INDEX idx_xCol FOR PROPERTY

    예제: 값 기반 쿼리

    다음 쿼리에서 부분 경로는 //를 사용하여 지정되므로 ISBN 값 기반의 조회 시에는 VALUE 인덱스를 사용하는 것이 유용합니다.

    SELECT xCol
    FROM docs
    WHERE xCol.exist (''//book/@ISBN[. = "0-7356-1588-2"]'') = 1

    VALUE 인덱스는 다음과 같이 생성됩니다.

    CREATE XML INDEX idx_xCol_Value on docs (xCol)
    USING XML INDEX idx_xCol FOR VALUE

    여러 파일 그룹의 XML 인덱스

    XML 인덱스는 기준 테이블과 결합되어 XML 인덱스 행이 해당 기준 테이블 행과 동일한 파일 그룹 및 테이블 파티션에 저장됩니다. 이를 위해서는 XML 블롭에 대한 큰 파일 그룹과 결합된 해당 XML 인덱스가 필요할 수 있습니다. CREATE TABLE 문의 TEXTIMAGE_ON 사양은 지정된 파일 그룹에 XML 블롭을 저장하고 XML 인덱스 행은 계속 기준 테이블과 결합되며, 큰 XML 노드 값은 XML 블롭과 동일한 파일 그룹에 존재합니다. 이렇게 하면 개별 파일 그룹의 크기를 줄이고 데이터를 보다 편리하게 관리할 수 있습니다. 예를 들어 행에 있는 비-XML 데이터가 XML 데이터 크기에 비해 작은 경우 이 기술을 사용하면 저장소를 보다 균등하게 분산할 수 있습니다.

    XML 열의 전체 텍스트 인덱스

    XML 열에 전체 텍스트 인덱스를 만들 수 있습니다. 이렇게 하면 XML 태그를 무시하면서 XML 값의 콘텐츠를 인덱싱합니다. 특성 값은 태그의 일부로 간주되기 때문에 전체 텍스트 인덱싱되지 않으며 요소 태그는 토큰 경계로 사용됩니다. 일부 시나리오에서는 XML 인덱스 사용과 전체 텍스트 검색을 조합할 수 있습니다.

  • SQL 전체 텍스트 검색을 사용하여 필요한 XML 값을 필터링합니다.
  • XML 열에서 XML 인덱스를 사용하는 XML 인스턴스를 쿼리합니다.

    예제: 전체 텍스트 검색과 XML 쿼리 조합

    XML 열에 전체 텍스트 인덱스를 만드는 단계는 다른 SQL 유형 열에 전체 텍스트 인덱스를 만드는 단계와 동일합니다. DDL 문은 다음과 같으며, 여기서 PK__docs__023D5A04는 테이블의 단일 열 기본 키 인덱스입니다.

    CREATE FULLTEXT CATALOG ft AS DEFAULT
    CREATE FULLTEXT INDEX ON dbo.docs (xCol) KEY INDEX PK__docs__023D5A04

    XML 열에서 전체 텍스트 인덱스를 만든 후 다음 쿼리는 XML 인스턴스에 "Secure"라는 단어가 책 제목에 포함되어 있는지 검사합니다.

    SELECT *
    FROM docs
    WHERE CONTAINS(xCol,''Secure'')
    AND xCol.exist(''/book/title/text()[contains(.,"Secure")]'') =1

    CONTAINS() 메서드는 전체 텍스트 인덱스를 사용하여 문서 내 어느 곳에든 "Secure"라는 단어가 포함된 XML 인스턴스를 분리합니다. exist() 절은 "Secure"라는 단어가 책 제목에 있는지 보장합니다.

    CONTAINS() 및 XQuery contains()를 사용한 전체 텍스트 검색은 서로 다른 의미를 갖고 있습니다. 후자는 하위 문자열 일치 검사이며, 전자는 형태소 분석을 사용한 토큰 일치 검사입니다. 따라서 제목에서 "run"이라는 문자열을 검색하는 경우 "run", "runs" 및 "running"은 모두 전체 텍스트 CONTAINS()와 XQuery contains()에 만족하기 때문에 이 검색에 포함됩니다. 하지만 위 쿼리에서 제목에 있는 "UnSecured"라는 단어는 검색되지 않습니다(전체 텍스트 CONTAINS()는 실패하지만 XQuery contains()에는 만족). 또한 전체 텍스트 검색은 단어의 형태소 분석을 사용하지만 XQuery contains()는 문자열 검색을 수행합니다. 일반적으로 완전한 하위 문자열 검색의 경우 전체 텍스트 CONTAINS() 절을 제거해야 합니다. 이러한 차이점은 다음 예제에서 설명합니다.

    예제: 형태소 분석을 사용한 XML 값의 전체 텍스트 검색

    예제: XML 쿼리와 전체 텍스트 검색 조합에서의 XQuery contains() 검사는 일반적으로 제거할 수 없습니다. 다음 쿼리를 보십시오.

    SELECT *
    FROM docs
    WHERE CONTAINS(xCol,''run'')

    문서에서 "ran"이라는 단어는 형태소 분석 덕분에 검색 조건과 일치합니다. 또한 검색 컨텍스트는 XQuery를 사용하여 검사되지 않습니다.

    AXSD를 사용하여 전체 텍스트 인덱싱된 관계형 열로 XML을 분산할 경우 XML 뷰에 대한 XPath 쿼리는 기본 테이블에서 전체 텍스트 검색을 수행하지 않습니다.

    XML 열에 대한 전체 텍스트 인덱스에서 여러 언어 지원

    전체 열에 대해 하나의 단어 분리기만 포함할 수 있는 nvarchar 또는 varchar 열과 달리 XML 데이터 형식 열은 XML 요소에 대한 xml:lang 특성을 사용하여 여러 언어의 단어 분리기를 지원합니다. 지정된 언어의 단어 분리기는 해당 요소의 콘텐츠에서 사용됩니다. 하위 요소는 xml:lang 특성에 다른 언어를 지정할 수 있습니다. 따라서 여러 XML 인스턴스 뿐만 아니라 단일 XML 인스턴스에도 여러 단어 분리기가 포함될 수 있습니다.

    이를 통해 여러 가지 흥미 있는 가능성이 도출됩니다. 예를 들어 Word 2003 문서에는 여러 언어로 된 섹션이 포함될 수 있습니다. WordML XML 표현의 문서를 XML 데이터 형식 열로 저장할 수 있으며 전체 텍스트 인덱싱에 적합한 언어 단어 분리기를 사용할 수 있습니다.

    전체 텍스트 쿼리는 다음 예제에서와 같이 사용할 언어를 지정할 수 있습니다.

    예제: 전체 텍스트 검색의 언어 지정

    다음 쿼리는 전체 텍스트 검색이 독일어로 수행되도록 지정합니다.

    SELECT * FROM docs
    WHERE contains (xCol, ''Visionen'', LANGUAGE ''German'')

    속성 승격

    쿼리가 주로 작은 수의 요소 및 특성 값에 대해 만들어진 경우(예: 고객 ID로 고객 찾기, 즉 /Customer/@CustId 값이 지정된 경우) 이러한 값을 관계형 열로 승격할 수 있습니다. 이러한 방식은 전체 XML 인스턴스가 검색되는 동안 XML의 작은 부분에 대해서 쿼리가 실행되는 경우에 유용합니다. XML 열에 대한 XML 인덱스 생성까지는 필요하지 않으며 대신 승격된 열을 인덱싱할 수 있습니다. 쿼리는 승격된 열을 사용하도록 작성되어야 합니다. 즉, 쿼리 최적화 프로그램에서 XML 열의 쿼리를 승격된 열로 다시 대상화하지 않습니다.

    승격된 열은 같은 테이블이나 별개의 테이블의 계산된 열이거나 테이블에 있는 사용자 유지 관리 열일 수 있습니다. 이러한 방식은 단일 값(즉, 단일 값 속성)이 각 XML 인스턴스로부터 승격되는 경우에 적합합니다. 하지만 다중 값 속성의 경우 아래에 설명된 대로 속성에 대한 별개의 테이블을 만들어야 합니다.

    XML 데이터 형식 기반 계산된 열

    XML 데이터 형식 메서드를 호출하는 UDF(사용자 정의 함수)를 사용하여 계산된 열을 만들 수 있습니다. 계산된 열 유형은 XML을 포함한 임의의 SQL 유형일 수 있습니다. 이에 대해서는 다음 예제에서 설명합니다.

    예제: XML 데이터 형식 메서드 기반 계산된 열

    책의 ISBN에 대한 사용자 정의 함수를 만듭니다

    CREATE FUNCTION udf_get_book_ISBN (@xData xml)
    RETURNS varchar(20)
    BEGIN
    DECLARE @ISBN varchar(20)
    SELECT @ISBN = @xData.value(''/book[1]/@ISBN'', ''varchar(20)'')
    RETURN @ISBN
    END

    ISBN에 대한 테이블에 계산된 열을 추가합니다.

    ALTER TABLE docs
    ADD ISBN AS dbo.udf_get_book_ISBN(xCol)

    계산된 열은 일반적인 방식으로 인덱싱할 수 있습니다.

    예제: XML 데이터 형식 메서드 기반 계산된 열에 대한 쿼리

    ISBN이 0-7356-1588-2인 을 가져오기 위해 XML 열에 대한 다음 쿼리

    SELECT pk, xCol
    FROM docs
    WHERE xCol.exist (''/book[@ISBN = "0-7356-1588-2"]'') = 1

    이는 다음과 같이 계산된 열을 사용하도록 다시 작성될 수 있습니다.

    SELECT pk, xCol
    FROM docs
    WHERE ISBN = ''0-7356-1588-2''

    사용자 정의 함수를 만들어서 XML 데이터 형식을 반환하고 UDF를 사용하여 계산된 열을 만들 수 있습니다. 하지만 XML 계산된 열에 대해서는 XML 인덱스를 만들 수 없습니다.

    속성 테이블 만들기

    XML 데이터로부터 일부 다중 값 속성을 하나 이상의 테이블로 승격시키고, 해당 테이블에서 인덱스를 만들고, 이를 사용하도록 쿼리를 다시 대상화할 수 있습니다. 일반적인 시나리오는 적은 수의 속성으로 대부분의 쿼리 작업을 포괄하는 것입니다. 다음과 같이 할 수 있습니다.
    • 다중 값 속성을 유지하는 하나 이상의 테이블을 만듭니다. 테이블 당 하나의 속성을 저장하고 기준 테이블과의 백 조인을 위해 속성 테이블에서 기준 테이블의 기본 키를 복제하는 것이 편리합니다.
    • 속성의 상대적 순서를 유지하려면 상대적 순서에 대한 별개의 열을 사용해야 합니다.
    • 속성 테이블을 유지하기 위해 XML 열에 트리거를 만듭니다. 트리거 내에서 다음 중 하나를 수행하십시오.
      . nodes() 및 value()와 같은 XML 데이터 형식 메서드를 사용하여 속성 테이블의 행을 삽입하고 삭제합니다. nodes() 메서드에 대한 자세한 내용은 Value(), nodes() 및 OpenXML() 섹션을 참조하십시오.
      . CLR에서 스트리밍 테이블 값 함수를 만들어서 속성 테이블의 행을 삽입하고 삭제합니다.

    • 속성 테이블에 대한 SQL 액세스와 기준 테이블의 XML 열에 대한 XML 액세스를 위한 쿼리를 작성하고 기본 키를 사용하는 테이블 간 조인을 포함시킵니다.

    예제: 속성 테이블 만들기

    저자의 이름을 승격시키는 경우를 가정해 보십시오. 책에는 한 명 이상의 저자가 있으므로 이름은 다중 값 속성입니다. 각 이름은 속성 테이블의 별개의 행에 저장됩니다. 기준 테이블의 기본 키는 백 조인을 위해 속성 테이블에 중복됩니다.

    CREATE TABLE tblPropAuthor (propPK int, propAuthor varchar(max))

    예제: XML 인스턴스로부터 행 집합을 생성하기 위해 사용자 정의 함수 만들기

    아래의 테이블 값 사용자 정의 함수 udf_XML2Table은 기본 키 값과 XML 인스턴스를 받아들입니다. 이 함수는 요소의 모든 저자에 대한 이름을 검색하고 기본 키 및 이름 쌍이 포함된 행 집합을 반환합니다. 래퍼 사용자 정의 함수를 사용하지 않고 XML 데이터 형식 메서드 기반의 계산된 열을 인덱싱하는 방식은 SQL Server 2005에서 지원되지 않습니다.

    CREATE FUNCTION udf_XML2Table (@pk int, @xCol xml)
    RETURNS table WITH SCHEMABINDING
    AS RETURN(
    select @pk as PropPK, nref.value(''.'', ''varchar(max)'') as propAuthor
    from @xCol.nodes(''/book/author/first-name'') R(nref)
    )

    예제: 속성 테이블을 채우기 위해 트리거 만들기

    Insert 트리거: 속성 테이블에 행을 삽입합니다.

    CREATE TRIGGER trg_docs_INS on docs FOR INSERT
    AS
    BEGIN
    insert into tblPropAuthor
    select p.*
    from inserted as I CROSS APPLY
    dbo.udf_XML2Table(I.pk, I.xCol) as P
    END

    Delete 트리거: 삭제되는 행의 기본 키 값에 따라 속성 테이블에서 행을 삭제합니다.

    create trigger trg_docs_DEL on docs for delete
    as
    begin
    delete tblPropAuthor where propPK IN
    (select p.PropPK
    from deleted as D CROSS APPLY
    dbo.udf_XML2Table(D.pk, D.xCol) as P
    )
    end

    Update 트리거: 업데이트된 XML 인스턴스에 따라 속성 테이블에서 기존 행을 삭제하고 속성 테이블에 새 행을 삽입합니다.

    create trigger trg_docs_UPD
    on docs
    for update
    as
    if update(xCol) or update(pk)
    begin
    delete tblPropAuthor where propPK IN
    (select p.PropPK
    from deleted as D CROSS APPLY
    dbo.udf_XML2Table(D.pk, D.xCol) as P
    )
    insert into tblPropAuthor
    select p.*
    from inserted as I CROSS APPLY
    dbo.udf_XML2Table(i.pk, i.xCol) as P
    end

    예제: 저자의 이름이 "David"인 XML 인스턴스 찾기

    쿼리를 XML 열에 표현할 수 있습니다. 또는 아래와 같이 "David"라는 이름을 속성 테이블에서 검색하고 기준 테이블로 백 조인을 수행하여 XML 인스턴스를 반환할 수 있습니다.

    SELECT xCol
    FROM docs JOIN tblPropAuthor ON docs.pk = tblPropAuthor.propPK
    WHERE tblPropAuthor.propAuthor = ''David''

    예제: CLR 스트리밍 테이블 값 함수를 사용한 솔루션

    이 솔루션은 다음과 같은 단계로 구성됩니다.

    1. IEnumerator를 구현하고 XML 인스턴스에 간단한 경로 식을 사용하여 스트리밍 테이블 값 출력을 생성하기 위한 InitMethod 메서드가 포함된 CLR 클래스 CXmlStreamingTVF를 정의합니다.

    2. 어셈블리와 Transact-SQL UDF(사용자 정의 함수)를 만들어서 CLR 클래스를 호출합니다.

    3. UDF를 사용하여 삽입, 업데이트 및 삭제 트리거를 정의하여 속성 테이블을 유지 관리합니다.

    먼저 아래와 같이 스트리밍 CLR 함수를 만듭니다. XML 데이터 형식은 ADO.NET에서 관리 클래스 SqlXml로 제공되며 XmlReader를 반환하는 메서드 CreateReader()를 지원합니다.

    using System;
    using System.Xml;
    using System.IO;
    using System.Data;
    using System.Data.Sql;
    using System.Data.SqlClient;
    using System.Data.SqlTypes;
    using Microsoft.SqlServer.Server;
    using System.Collections;

    public class CXmlStreamingTVF : IEnumerator {
    private XmlReader m_reader;
    private SqlXml m_doc;
    private string m_name;
    private string[] m_path;
    private int m_pathLoc;

    public CXmlStreamingTVF (SqlXml doc, string simplePath) {
    m_doc = doc;
    m_reader = m_doc.CreateReader();
    m_path = simplePath.Split(new char[]{''/''});
    m_pathLoc = m_path.Length-1;
    }

    //Three IEnumerator methods.
    //Custom code for Navigating the document for a simple path.
    public bool MoveNext () {
    bool new_row = false;
    while (!new_row && !m_reader.EOF) {
    m_reader.Read();
    if (m_reader.LocalName==m_path[m_pathLoc] &&
    m_pathLoc==m_path.Length-1 &&
    m_reader.NodeType==XmlNodeType.Element) {
    m_name = m_reader.ReadString();
    new_row = true;
    }
    else if (m_reader.LocalName==m_path[m_pathLoc] &&
    m_reader.NodeType==XmlNodeType.Element &&
    m_reader.IsEmptyElement==false) {
    if (m_pathLoc==1 && m_reader.Depth!=0)
    continue;
    m_pathLoc++;
    }
    else if (m_pathLoc!=1 &&
    m_reader.LocalName==m_path[m_pathLoc-1] &&
    m_reader.NodeType==XmlNodeType.EndElement) {
    m_pathLoc--;
    }
    }
    return new_row;
    }

    public object Current { get { return this; } }
    public void Reset () {
    m_reader.Close();
    m_reader = m_doc.CreateReader();
    }

    [SqlFunctionAttribute (FillRowMethodName="CLROpenXml")]
    public static IEnumerator InitMethod (SqlXml doc, string simplePath)
    {
    return new CXmlStreamingTVF(doc, simplePath);
    }

    public static void CLROpenXml(Object obj, out string name) {
    CXmlStreamingTVF stream = (CXmlStreamingTVF) obj;
    name = stream.m_name;
    }
    }

    그런 다음 어셈블리 및 CLR 메서드 InitMethod()에 해당하는 Transact-SQL 사용자 정의 함수 SQL_streaming_xml_tvf를 만듭니다.

    CREATE ASSEMBLY CLRXML
    FROM ''C:\temp\StreamingTVF.dll''
    WITH PERMISSION_SET = SAFE

    CREATE FUNCTION SQL_streaming_xml_tvf (
    @xData XML, @xPath nvarchar(max))
    RETURNS table (FirstName nvarchar(max))
    AS
    EXTERNAL NAME [CLRXML].[CXmlStreamingTVF].[InitMethod]

    UDF는 행 집합 생성을 위해 테이블 값 함수 CLR_udf_XML2Table을 정의하는 데 사용됩니다.

    create function CLR_udf_XML2Table (@pk int, @xCol xml)
    returns @ret_Table table (FK int, FirstName varchar(max))
    with schemabinding
    as
    begin
    insert into @ret_Table
    select @pk, FirstName
    FROM SQL_streaming_xml_tvf (@xCol, ''/book/author/first-name'')
    return
    end

    마지막으로 CLR_udf_XML2Table replacing udf_XML2Table 함수로 "속성 테이블을 채우기 위한 트리거 만들기" 예제에서 표시된 대로 트리거를 정의합니다. 그러면 삽입 트리거가 다음과 같이 됩니다.

    create trigger CLR_trg_docs_INS on docs for insert
    as
    begin
    insert into tblPropAuthor
    select p.*
    from inserted as I CROSS APPLY
    dbo.CLR_udf_XML2Table(I.pk, I.xCol) as P
    end

    삭제 및 업데이트 트리거는 비-CLR 함수와 비슷하며 단순히 udf_XML2Table() 함수를 CLR_udf_XML2Table() 함수로 교체하면 얻을 수 있습니다.

    이러한 두 대안의 장단점

    속성 테이블에 행을 생성, 삭제 및 수정하는 데 사용된 함수 udf_XML2Table()이 CPU 사용량이 높은 경우 CLR 기반 접근 방식이 일반적으로 더 빠릅니다. 여기에는 매우 복잡한 구조의 XML 데이터가 포함되어 XML 구문 분석의 계산 비용이 높습니다. udf_XML2Table() 함수의 비용이 낮은 경우에는 차이점이 줄어듭니다. 크기가 작은 XML과 간단한 경로 식의 경우 컨텍스트 변환 비용은 CLR 기반 솔루션에 더욱 나쁜 영향을 줄 수 있습니다.

    Transact-SQL 및 XQuery 기반 솔루션과는 달리 CLR 기반 솔루션의 경로 식은 하드 코딩됩니다. 이러한 방식은 경로 식이 미리 알려져 있는 경우 제대로 작동합니다. 그 외 다른 모든 경우에는 Transact-SQL 및 XQuery 기반 솔루션이 유일한 방법입니다.

    XML 스키마 컬렉션

    XML 스키마 컬렉션은 관계형 스키마 범위에 포함되는 메타데이터 엔터티이며 를 통해 관련되거나 관련되지 않은 하나 이상의 XML 스키마가 포함됩니다. XML 스키마 컬렉션 내의 개별 XML 스키마는 해당 대상 네임스페이스를 사용하여 식별됩니다.

    XML 스키마 컬렉션은 CREATE XML SCHEMA COLLECTION 구문을 사용하여 생성되며 하나 이상의 XML 스키마를 제공합니다. 기존 스키마에 보다 많은 XML 스키마 구성 요소를 추가할 수 있으며 ALTER XML SCHEMA COLLECTION 구문을 사용하여 XML 스키마 컬렉션에 더 많은 스키마를 추가할 수 있습니다. XML 스키마 컬렉션은 SQL Server 2005의 보안 모델을 사용하여 모든 SQL 개체와 같은 방식으로 보안됩니다.

    다중 유형 열

    XML 스키마 컬렉션 C는 다중 XML 스키마에 따라 XML 열 xCol을 형식화합니다. 또한 DOCUMENT 또는 CONTENT 플래그는 XML 트리 또는 조각이 각각 xCol 열에 저장될 수 있는지 여부를 지정합니다.

    DOCUMENT의 경우 각 XML 인스턴스는 인스턴스의 유효성을 검사하고 형식 있는 해당 최상위 요소에 대한 대상 네임스페이스를 인스턴스에서 지정합니다. 반대로 CONTENT의 경우에는 각 최상위 요소가 대상 네임스페이스 중 하나를 C에서 지정할 수 있습니다. XML 인스턴스는 인스턴스에서 발생하는 모든 대상 네임스페이스에 따라 유효성이 검사되고 형식화됩니다.

    스키마 발전

    XML 스키마 컬렉션은 XML 열, 변수 및 매개 변수를 형식화하는 데 사용됩니다. XML 스키마 컬렉션은 XML 스키마 발전을 위한 메커니즘을 제공합니다. 대상 네임스페이스가 BOOK-V1인 XML 스키마를 XML 스키마 컬렉션 C에 추가한다고 가정해 보십시오. C를 사용하여 형식 있는 XML 열 xCol은 BOOK-V1 스키마를 준수하는 XML 데이터를 저장할 수 있습니다.

    응용 프로그램에서 복잡한 유형의 정의 및 최상위 요소 선언과 같은 새로운 스키마 구성 요소로 XML 스키마를 확장한다고 가정해 보십시오. 이러한 새 스키마 구성 요소를 BOOK-V1 스키마에 추가할 수 있으며 이 구성 요소에는 xCol 열에 있는 기존 XML에 대한 유효성 검사를 다시 수행할 필요가 없습니다.

    나중에 응용 프로그램에서 대상 네임스페이스 BOOK-V2를 선택하도록 XML 스키마의 새 버전을 제공한다고 가정해 보십시오. 이 XML 스키마를 C에 추가할 수 있습니다. XML 열은 BOOK-V1 및 BOOK-V2의 인스턴스를 모두 저장할 수 있으며 이러한 네임스페이스를 준수하는 XML 인스턴스에 있는 쿼리 및 데이터 수정을 실행합니다.

    와일드카드 섹션에 허용되지 않는 Lax 유효성 검사

    XML 스키마 프로세서는 와일드 카드 섹션(xs:any 및 xs:anyAttribute) 및 xs:anyType에서 lax 유효성 검사를 지원하지 않습니다. 와일드카드 섹션의 경우 XML 스키마는 processContents = "strict" 또는 processContents = "skip"을 지정할 수 있습니다. xs:anyType의 경우 엄격한 유효성 검사만 지원됩니다.

    엄격한 유효성 검사는 이러한 스키마 구성 요소를 인스턴스화하는 XML 노드와 관련된 보다 정확한 유형 정보가 유효성 검사 중에 알려지고 쿼리 컴파일 중에 사용되도록 보장합니다. 건너뛰기 의미는 형식화 정보가 손실되고 해당 노드는 형식 없는 상태로 취급됩니다(요소의 경우는 xdt:untyped 및 특성의 경우는 xdt:untypedAtomic).

    xs:anyType에 대한 건너뛰기 의미가 필요한 경우 아래와 같이 xs:any 및 processContents = "skip"이 포함된 xs:anyAttribute가 사용되는 새로운 복합 유형을 사용하십시오.






    xs:datetime, xs:date 및 xs:time 사용

    xs:datetime, xs:data 및 xs:time 유형의 값은 ISO 8601 형식으로 지정되어야 하며 표준 시간대가 포함되어야 합니다. 그렇지 않으면 이러한 값에 대한 데이터 유효성 검사가 실패합니다. 따라서 2005-05-27T14:11:00.943Z는 xs:datetime 유형에 대한 올바른 값이지만 2005-05-27 14:11:00.943Z는 날짜 및 시간 구분자인 "T"가 없고, 2005-05-27T14:11:00.943은 표준 시간대가 없고, 2005-05-27 14:11:00.943은 시간 구분자와 표준 시간대가 없어서 올바른 값이 아닙니다. 이와 비슷하게 2005-05-27Z는 올바른 xs:date 값이지만 2005-05-27은 표준 시간대가 지정되지 않아서 올바른 값이 아닙니다.

    형식 없는 XML 데이터에는 응용 프로그램이 SQL 유형의 dateTime 또는 smallDateTime으로 변환하려는 날짜, 시간 및 날짜/시간 값이 포함될 수 있습니다. 이러한 날짜, 시간 및 날짜/시간 값은 ISO 8601 형식을 준수하지 않거나 표준 시간대를 포함하지 않을 수 있습니다. 이와 비슷하게 형식 있는 XML에는 xs:date, xs:time 및 xs:dateTime 이외의 유형의 값(예: xs:string)이 포함될 수 있습니다. 두 경우 모두 값을 먼저 [n]varchar로 변환한 후 다음 예제에서와 같이 SQL datetime 또는 smalldatetime으로 변환해야 합니다.

    예제: 형식 없는 XML에서 날짜/시간 값 추출

    다음 데이터로부터 CreationTime 특성의 값을 가져오기 위해

    declare @var xml
    select @var =
    ''
    LastExecutionTime="2005-05-19 14:11:00.943"
    CreationTime="2005-05-19 14:11:00.913"/>




    ''

    value() 메서드가 사용되어 이후에 SQL datetime 유형으로 캐스트된 nvarchar(64) 값을 검색합니다.

    select cast (@var.value(
    ''(/QueryExecutionStats /GeneralStats/@CreationTime)[1]'',
    ''nvarchar(max)'') AS datetime) as creation_time

    사용법

    XML 데이터 로드

    SQL Server 2000에서 SQL Server 2005로 XML 데이터 전송
    XML 데이터는 여러 가지 방법으로 SQL Server 2005로 전송할 수 있습니다. 여기에서는 이에 대한 일부 옵션을 다룹니다.
  • 데이터가 SQL Server 2000 데이터베이스의 [n]text 또는 이미지 열에 있는 경우 DTS 등을 사용하여 테이블을 SQL Server 2005 데이터베이스로 가져옵니다. 열 유형을 각각 [n]varchar(max) 또는 varbinary(max)로 바꾼 다음 ALTER TABLE 문을 사용하여 XML로 바꿉니다.
  • bcp out을 사용하여 SQL Server 2000의 데이터를 대량 복사하고 bcp in을 사용하여 SQL Server 2005 데이터베이스로 대량 삽입합니다.
  • SQL Server 2000 데이터베이스의 관계형 열에 데이터가 있는 경우 [n]text 열로 테이블을 새로 만들고 필요에 따라 행 식별자에 대한 기본 키 열을 만듭니다. 클라이언트측 프로그래밍을 사용하여 서버에서 FOR XML로 생성된 XML을 검색하고 이를 [n]text 열에 기록합니다. 그런 다음 위에 언급한 기술을 사용하여 데이터를 SQL Server 2005 데이터베이스로 전송합니다. SQL Server 2005 데이터베이스의 XML 열로 XML을 직접 기록하도록 선택할 수도 있습니다.

    예제: 열 유형을 XML로 변경


    테이블 R에 있는 [n]text, [n]varchar, varbinary 또는 XML 열 XYZ를 XML 스키마 컬렉션 bookCollection을 사용하여 형식 있는 XML로 바꾼다고 가정해 보십시오. 다음 문은 이러한 유형 변환을 수행합니다.

    ALTER TABLE R ALTER COLUMN XYZ XML (bookCollection)

    XML 스키마 컬렉션이 지정되지 않은 경우 대상은 형식 없는 XML입니다.


    텍스트 인코딩


    SQL Server 2005는 XML 데이터를 유니코드(UTF-16)로 저장합니다. 서버에서 검색된 XML 데이터는 UTF-16 인코딩으로 되고 데이터 검색 시 다른 인코딩이 필요한 경우 응용 프로그램에서 검색된 UTF-16 데이터에 대해 필요한 변환을 수행해야 합니다.

    문자열 유형을 XML 데이터 형식으로 변환할 경우 SQL Server 2005는 원본 문자열의 데이터 정렬에 대한 코드 페이지를 사용하여 인코딩을 결정합니다. XML 인코딩 정보가 XML 선언(예: <?xml version="1.0" encoding="windows-1256"?>)에 있는 "encoding" 특성을 사용하여 지정된 경우 인코딩은 문자열의 코드 페이지와 호환되어야 합니다. 문자열 데이터는 이러한 두 데이터 정렬이 호환되는 한 XML 파서에서 제대로 구문 분석될 수 있습니다. 그렇지 않으면 오류가 발생하거나 잘못된 데이터가 로드됩니다. 클라이언트 응용 프로그램이 XML 데이터 형식으로의 변환을 위해 서버에 문자열 값을 보낼 때에도 같은 동작이 발생합니다.

    일부 경우에는 여러 인코딩의 XML 데이터가 있거나 인코딩에 대한 사전 지식이 없을 수도 있습니다. 이러한 경우의 권장 방법은 XML 데이터를 이진 데이터 형식(예: varbinary(max))으로 제공하는 것입니다. 서버는 데이터 스트림(0xFFFE는 UTF-16을 나타냄)의 바이트 순서 표시 또는 XML 선언으로부터(있는 경우) 인코딩을 파생합니다. 그 결과 XML 매개 변수에서 XML 인코딩 불일치를 피하기 위한 가장 쉬운 방법은 클라이언트로부터 데이터를 네이티브 XML(ADO.NET의 SqlXml 클래스 사용) 또는 이진 유형으로 보내거나 [var]binary 데이터 형식을 서버에서 XML로 변환하는 것입니다.

    이러한 규칙을 요약하면 다음과 같습니다.


  • 텍스트 XML이 유니코드(UCS-2, UTF-16)인 경우 이를 XML 열, 변수 또는 매개 변수로 할당해도 아무 문제가 발생하지 않습니다.
  • 인코딩이 유니코드가 아니고 원본 코드 페이지로 인해 내재적인 경우 데이터베이스의 문자열 코드 페이지는 로드하려는(필요한 경우 COLLATE 사용) 코드 포인트와 같거나 호환되어야 합니다. 이러한 서버 코드 페이지가 없으면 명시적 XML 선언을 추가하여 적합한 인코딩을 지정해야 합니다.
  • 명시적 인코딩을 사용하려면 코드 페이지와 상호 작용이 없는 varbinary 유형을 사용하거나 적합한 코드 페이지의 문자열 유형을 사용합니다. 그런 다음 데이터를 XML 열, 변수 또는 매개 변수로 할당합니다.

    그래서 UTF-8로 전달하려는 경우 varbinary(max)로 전달하는 것이 가장 안전합니다. UTF-16 데이터는 바이트 순서 표시가 필요하지 않은 nvarchar(max)로 전달하거나 UTF-16 인코딩을 표시하도록 처음 두 바이트로 바이트 순서 표시가 0xFFFE인 varbinary(max)로 전달할 수 있습니다.


    XML 데이터 대량 로드


    SQL Server의 대량 로드 기능인 BCP, OPENROWSET 및 BULK INSERT를 사용하여 XML 데이터를 서버로 대량 로드할 수 있습니다. OPENROWSET을 사용하면 데이터를 파일에서 XML 열로 로드할 수 있습니다. 다음 예제에서는 이러한 점에 대해 설명합니다.


    예제: 파일로부터 XML 로드


    이 예제에서는 행을 테이블 문서에 삽입하는 방법을 보여 줍니다. XML 열의 값은 C:\temp\xmlfile.xml 파일로부터 이진 LOB(BLOB)로 로드되고 pk 열에 값 10이 제공됩니다. 파일은 CLOB나 NCLOB 대신 BLOB으로 로드되어 XML 문서가 인코딩되는 모든 인코딩을 수용합니다.

    INSERT INTO docs
    SELECT 10, xCol
    FROM (SELECT *
    FROM OPENROWSET (BULK ''C:\temp\xmlfile.xml'', SINGLE_BLOB)
    AS xCol) AS R(xCol)


    비-이진 데이터 정렬


    XML 데이터 형식에 사용되는 XML 데이터 정렬은 이진 데이터 정렬이며 대/소문자를 구분합니다(유니코드 코드 포인트 데이터 정렬). 응용 프로그램은 대/소문자 구분 검색과 같은 서로 다른 요구 사항을 갖고 있습니다. 이러한 목표는 적합한 문자열 값을 적합한 데이터 정렬과 함께 varchar 유형의 계산된 열로 승격함으로써 달성할 수 있습니다. 데이터 정렬 종속 작업에 대한 계산 열을 쿼리합니다. 또한 XML 열에 독일어 및 중국어 데이터 문자열이 들어 있다고 가정합니다. 이러한 각 데이터 정렬에 관련된 작업을 각 언어별로 하나씩 두개의 계산된 열에서 사용할 수 있습니다.


    XQuery 및 유형 유추


    Transact-SQL에 포함된 XQuery(http://www.w3.org/TR/xquery/ (영문))는 XML 데이터 형식 쿼리를 위해 지원되는 언어입니다. 이 언어는 Microsoft를 비롯한 주요 데이터베이스 공급업체의 참여로 W3C(World Wide Web Consortium)에서 개발 중에 있습니다(현재 마지막 협의 중). 여기에는 탐색 언어로 XPath 2.0이 포함됩니다. 데이터 수정을 위한 언어 구성은 XML 데이터 형식에서도 사용할 수 있습니다. SQL Server 2005에서 지원되는 XQuery 구성, 함수 및 연산자에 대한 정보는 온라인 설명서를 참조하십시오.


    오류 모델


    컴파일 오류는 의미상 잘못된 XQuery 식 및 XML DML 문으로부터 반환됩니다. 컴파일 단계에서는 XQuery 식 및 DML 문의 정적 유형이 올바른지 여부를 검사하고 형식 있는 XML에 대한 유형 유추를 위해 XML 스키마를 사용합니다. 여기에서는 식이 유형 안전도 위반으로 인해 런타임에 식이 실패할 수 있는 경우 정적 유형 오류를 발생시킵니다. 정적 오류의 예로는 정수에 문자열 추가와 형식 있는 데이터에 대해 존재하지 않는 노드의 쿼리를 들 수 있습니다.

    W3C 표준과는 달리 XQuery 런타임 오류는 빈 시퀀스로 변환되며 호출 컨텍스트에 따라 쿼리 결과에 빈 XML 또는 NULL로 채워질 수 있습니다.

    적합한 유형에 대한 명시적 형변환을 통해 사용자는 런타임 캐스트 오류가 빈 시퀀스로 변환되더라도 정적 오류와 관련된 문제를 해결할 수 있습니다.

    다음 하위 섹션에서는 유형 검사에 대해 자세히 설명합니다.


    단일 검사


    단일 항목이 필요한 위치 단계, 함수 매개 변수 및 연산자(예: eq)는 컴파일러에서 단일 항목이 런타임에 보장되는지 확인할 수 없는 경우 오류를 반환합니다. 문제는 형식 없는 데이터에서 자주 발생하며 형식 있는 데이터의 경우에도 일부 문제가 발생합니다. 예를 들어 특성 조회를 위해서는 단일 부모 요소가 필요하며 단일 부모 노드의 순서별 선택이 적합합니다. 특성 값 추출을 위한 nodes()-value() 조합에 대한 평가(Value(), nodes() 및 OpenXML() 참조)에는 다음 예제에서와 같이 nodes() 메서드가 단일 컨텍스트 항목을 제공하기 때문에 순서 사양이 필요하지 않습니다.


    예제: 알려진 단일 항목


    이 예제에서 nodes() 메서드는 각 요소에 대한 별개의 행을 생성합니다. nodes() 메서드에 대한 자세한 내용은 Value(), nodes() 및 OpenXML() 섹션을 참조하십시오. 노드에서 평가된 value() 메서드는 특성으로서 단일 항목인 @genre의 값을 추출합니다.

    SELECT nref.value(''@genre'', ''varchar(max)'') Genre
    FROM docs CROSS APPLY xCol.nodes(''//book'') AS R(nref)

    XML 스키마는 형식 있는 XML의 유형 검사에 사용됩니다. 노드가 XML 스키마에서 단일 항목으로 지정된 경우 컴파일러는 이 정보를 사용하고 오류가 발생하지 않습니다. 그렇지 않으면 순서별 단일 노드 선택이 필요합니다. 특히 /book//title과 같은 하위 항목 축을 사용하면 XML 스키마에 지정된 경우에도

    출처명: 한국마이크로소프트

  • 728x90

    1. 테이블의 IDENTITY값을 해당 번호부터 증가시킬 경우

    -->

    DBCC CHECKIDENT ('[TABLENAME]', RESEED, 20)

    2. 테이블의 IDENTITY값을 수동으로 저장할 경우

    -->

    SET IDENTITY_INSERT [TABLENAME] ON 를 사용하여 수동지정

    ID값을 지정한 후 INSERT/UPDATE

    SET IDENTITY_INSERT [TABLENAME] OFF 를 사용하여 자동지정

    출처 : Tong - ahnz님의 데이터베이스통

    728x90
    --**********************************************************************
    --      함수(날짜열)
    --**********************************************************************
    함수(날짜열)
    DATE구분 구분약자 DATE구분 구분약자
    year yy week wk
    quarter qq hour hh
    month mm minute mi
    day of year dy second ss
    day dd millisecond ms
    항목 내용
    GETDATE() 시스템에서 제공하는 날짜와 시간 값
    산술연산 -- date + NUMBER
    -- date - NUMBER


    --날짜는 계산할 수 있다.
    --getdate()
    select getdate()'현재날짜 & 시간'
    --getdate() -1, +1
    select getdate() - 1'어제 이 시간',
       getdate() + 1'내일 이 시간'
    --오늘부터 100일 전의 날짜를 검색!
    select getdate() - 100
    --회원테이블의 생일과 1200일째 되는 날을 검색! (Alias는 회원명, 생일, 12000일째)
    select mem_name "회원명", mem_bir "생일",
       getdate() + 1200 "1200일째"
     from member

    --**********************************************************************
    --   DATEADD 함수(날짜열)
    --**********************************************************************
    --DATEADD (Part,n,date)/Part부분에 n만큼 더한 date
    --      (DATE구분별로 모두 적용)  

    select getdate() '현재날짜 & 시간',
       dateadd(year, 1, getdate()) '1년뒤'
    --
    select getdate() '현재날짜 & 시간',
       dateadd(mm,-1, getdate()) '한달전'
    --
    select getdate()'현재날짜 & 시간',
       dateadd(dd, -50, getdate()) '50일전'
    --
    select getdate()'현재날짜 & 시간',
       dateadd(hour, 7, getdate()) '7시간 뒤'
    --
    select dateadd(yy, 1, '19990228') "1년후",
       dateadd(yy, 2, '19990228') "2년후",
       dateadd(yy, 3, '19990228') "3년후"
    --
    select dateadd(mm, 12, '19000228') "1년후",
       dateadd(mm, 24, '19000228') "2년후",
       dateadd(mm, 36, '19000228') "3년후"
    --오늘부터 6개월 전의 날짜를 검색!
    select dateadd(mm, -6, getdate())"6개월전의 날짜"
    --회원테이블에서 구길동회원의 생일과 12000일째 되는 날을 검색!
    --(DATEADD사용) (Alias는 회원명, 생일, 12000일째)
    --1.
    select mem_name"회원명", mem_bir"생일", dateadd(dd,1200,mem_bir)"1200일째" from member
       where mem_name like '구길동%'
    --2.
    select mem_name"회원명", mem_bir"생일", dateadd(dd,1200,mem_bir)"1200일째" from member
       where mem_name like '구길동'
    --3.
    select mem_name"회원명", mem_bir"생일", dateadd(dd,1200,mem_bir)"1200일째" from member
       where mem_name='구길동'

    --********************************************************************
    --      YEAR,MONTH,DAY,DATEDIFF함수(날짜열)
    --********************************************************************
    --year, month, day 해당일의 년월일 값을 돌려준다.
    select year(getdate())'year',
       month(getdate())'month',
       day(getdate())'day'
    --datediff(part,date1,date2)/두 날짜 사이의 DATE구분 사이 값을 계산함.
    --   (part, date1, date2)
    select datediff(dd,'1999/02/15','2000/02/15')'day',
       datediff(mm,'1999/02/15','2000/02/15')'month',
       datediff(yy,'1999/02/15','2000/02/15')'year'
    --
    select datediff(dd,'1999/11/06','2000/02/15')'day',
       datediff(mm,'1999/11/06','2000/02/15')'month',
       datediff(yy,'1999/11/06','2000/02/15')'year'
    --회원테이블의 생일과 오늘의 일수 차이를 검색! (Alias는 회원명, 생일, 오늘, 태어난지?)
    select mem_name"회원명", mem_bir"생일",
       getdate()"오늘",
       datediff(dd,mem_bir,getdate())"태어난지?"
     from member
    --본인의 생년월일과 오늘의 일수 차이를 비교 검색!
    select datediff(dd,'1972/08/14',getdate()) "차이는"

    --*******************************************************************
    --      Datename, Datepart함수(날짜열)
    --*******************************************************************
    --datename(part,date)/date에서 datepart부분의 ASCII값(구분문자)
    --datepart(part,date)/date에서 datepart부분의 정수값(구분숫자)
    select convert(char,getdate()), '<==현재날짜'
    select datename(yy,getdate()),datepart(yy,getdate()),'<==년도'
    select datename(qq,getdate()),datepart(qq,getdate()),'<==분기'
    select datename(mm,getdate()),datepart(mm,getdate()),'<==월'
    select datename(dy,getdate()),datepart(dy,getdate()),'<==일수'
    select datename(dd,getdate()),datepart(dd,getdate()),'<==해당일'
    select datename(wk,getdate()),datepart(wk,getdate()),'<==주수'
    select datename(dw,getdate()),datepart(dw,getdate()),'<==요일'
    select datename(hh,getdate()),datepart(hh,getdate()),'<==시간'
    --회원테이블에서 구길동회원의 생일의 DATENAME 과 DATEPART를 검색!
    --(Alias 는 회원명, 생일, 기타 上 同)
    select mem_name"회원명", mem_bir"생일",
       datename(dw,mem_bir)'기타上同',
       datepart(dw,mem_bir)'기타上同'
     from member
       where mem_name='구길동'
    /* 월 (2002년 2월)만 입력받아 해당월의 시작일과 종료일을 검색!
    Alias는 해당월, 시작일, 종료일) */
    --******************** 1. 2002-02-01의 1달 후 -1....
    select '200년02월' 해당월, '200-02-01' 시작일,
       dateadd(mm,1,'2000-02-01')-1 "종료일"
    --******************** 2. 2002-03-01의 1일 전....
    select '200년02월' 해당월, '200-02-01' "시작일",
       dateadd(dd,-1,'2000-03-01') "종료일"

    --*******************************************************************
    --      함수(Conversion)
    --*******************************************************************
    --cast, convert : 문자로 치환/number와 date를 문자로 치환.
    --   /간단한 형 변환(CAST)
    --   /날짜를 문자로 변환 시 여러가지 형식이 가능하므로 주로 사용하는 함수는(CONVERT)이다.
    select convert(char, 123456789) result1,
       cast(123456789 as char) result2
    --
    select convert(varchar,456789) result1,
       cast(456789 as varchar) result2,
       str(456789, 10) result3
    /* 숫자를 바꾸는 convert함수를 썼는데 2번째줄의 경우는 '300567'이라는 6섯자를
    'char(4)' 4자로 바꾸라는 것: 이것은 말이 안되므로 * 표시가 프린트됨!(주의 사항) */
    select convert(char(7),300567) result1,
       convert(char(4),300567) result2
    --
    select convert(char,getdate(),0) result1,
    cast(getdate() as char) result2
    --
    select convert(CHAR, GETDATE(), 109) RESULT
    select convert(CHAR, GETDATE(), 111) RESUlT
    select convert(CHAR(10), GETDATE(), 121) RESULT
    select convert(CHAR, GETDATE(), 114) RESULT

    --
    select convert(char, getdate(),0)
    select convert(char, getdate(),1)
    select convert(char, getdate(),2)
    select convert(char, getdate(),3)
    select convert(char, getdate(),4)
    select convert(char, getdate(),5)
    select convert(char, getdate(),6)
    select convert(char, getdate(),7)
    select convert(char, getdate(),8)
    select convert(char, getdate(),9)
    select convert(char, getdate(),10)
    select convert(char, getdate(),11)
    select convert(char, getdate(),12)
    select convert(char, getdate(),13)
    select convert(char, getdate(),14)
    --다음 아래는 ERR...
    select convert(char, getdate(),15)
    select convert(char, getdate(),16)
    select convert(char, getdate(),17)
    select convert(char, getdate(),18)
    select convert(char, getdate(),19)
    --여기까지 ERR...
    select convert(char, getdate(),20)
    select convert(char, getdate(),21)
    select convert(char, getdate(),22)
    select convert(char, getdate(),23)
    select convert(char, getdate(),24)
    select convert(char, getdate(),25)
    --여기서 또 아래는 ERR...
    select convert(char, getdate(),26)
    select convert(char, getdate(),27)
    select convert(char, getdate(),28)
    select convert(char, getdate(),29)
    select convert(char, getdate(),30)
    select convert(char, getdate(),31)
    select convert(char, getdate(),32)
    select convert(char, getdate(),33)
    select convert(char, getdate(),34)
    -- .
    -- .
    -- .
    select convert(char, getdate(),99)
    --99까지 ERR
    --100단위로 위와 똑같은 숫자안에 포함되어 쓰인다.
    --120과 121번의 경우는 우리나라에서 많이 쓴다. 그냥 21번을 쓰기도 한다.
    select convert(char, getdate(),120)
    select convert(char, getdate(),121)
    /* 위에서 일반숫자형을 문자로 변환할 때 변환되어지려는 값과 변환하려는 값의
    자릿숫자값이 다르면 에러가 났었지만, 날짜형은 그 자릿수만큼 잘라서 출력한다! */
    --다음은 그 예이다.
    --1. 날짜형 변환의 예)
    select convert(char(10), getdate(),121)
    --2. 숫자형 변환의 예)
    select convert(char(7),300567) result1,
    convert(char(4),300567) result2
    --3. 문자형를 자르기 예)
    select convert(char(10), 'abcdefghijklmnopqrstuvwxyz')

    --122~에서부턴 형식이 맞지 않는다 ERR
    select convert(char, getdate(),122)
    select convert(char, getdate(),123)
    select convert(char, getdate(),124)
    select convert(char, getdate(),125)
       :
       :

    --상품테이블에서 상품코드와 상품명을 연결하여 30자리로 치환하여 검색!
    --Alias는 상품코드, 상품명, 치환상품명)
    select prod_id 상품코드, prod_name 상품명,
       convert(char(30), prod_id + prod_name) 치환상품명
      from prod

    --***********************************************
    --      conversion
    --***********************************************
    --예제1) // datetime, smalldatetime, decimalzero, decimalpoint, numericzero, numericpoint

    select convert(char(8), getdate(),112) result
    --
    select convert(datetime, '2001-01-01') DATETIME
    --
    select smalldatetime = convert(smalldatetime,'2001-01-01')
    --
    select decimalzero = convert(decimal(15),12345678912345)
    --
    select decimalpoint = convert(decimal(17,2),123456789012345.11)
    --
    select numericzero = convert(numeric,123456789012345)
    select numericpoint = convert(numeric(17,2),123456789012345.11)

    --예제2) // float, int, smallint, tinyint, money

    select convert(float,123456789012345) float
    --
    select convert(float,10/3.0) float
    --
    select convert(int,1234567890) int
    --
    select convert(smallint,12345) smallint
    --
    select convert(tinyint, 123) tinyint
    --
    select convert(money,123456789012345) money

    --***********************************************************************
    --함수(Conversion) : 숫자로 치환
    --***********************************************************************
    --   숫자로 치환 // 모양만 수치인 문자를 NUMBER로 치환
    --   /모양만 수치라면 연산에는 영향이 없다.
    select convert(numeric,'123456.9') result1,
       cast('123456.9' as numeric) result2
    --
    select convert(numeric(10,2),'123456.9') result1,
       cast('123456.9' as numeric(10,2)) result2
    --
    select convert(float,'123456.9') result1,
       cast('123456.9' as float) result2
    --
    select convert(decimal,'123456.9') result1,
       cast('123456.9' as decimal) result2
    --*****************************************************
    /* 회원테이블에서 이쁜이 회원의 회원ID(b001) 2~4문자열을 숫자형으로 치환한 후 10을 더하여
     새로운 회원ID(b011)로 조합(Alias는 회원ID, 조합회원ID) */

        회원ID     조합회원ID
       -------   ------------
         b001          b011
       (1 row(s) affected)
    --1. 방법1
    select mem_id "회원ID",
       left(mem_id,1) + right(convert(char(4),right(mem_id,3) + 1010),3) "조합회원ID"
       from member
       where mem_name = '이쁜이'
    --2. 방법2
    select mem_id "회원ID",
       left(mem_id,1) + right(convert(char(4),substring(mem_id,2,4)+1010),3) "조합회원ID"
       from member
       where mem_name = '이쁜이'
    --***********************************************************************
    --      함수(conversion) : 날짜로 치환
    --***********************************************************************
    --날짜로 치환/모양만 날짜형인 문자를 DATE로 치환
    select'19990101'result1,'1999-01-01'result2,
       '1999-01-01 00:00:00.000'result3
    --
    select convert(datetime,'20201025',112)" result1",
       convert(datetime,'20201025')" result2",
       convert(datetime,'2020-10-25 10:15:20.000') " result3",
       cast('2020-10-25 10:15:20.000' as datetime) " result4"
    --
    --회원테이블에서 주민등록번호1을 날짜로 치환한 후 검색!
    --(Alias는 회원명, 주민등록번호1, 치환날짜)
    select mem_name 회원명, mem_regno1 주민등록번호1,
       convert(datetime,'19'+mem_regno1) 치환날짜
       from member
    --1900년대 사람들만 있다고 가정했을 경우 19를 더해서 밀레니엄버그를 없앨 수 가 있다.
    --회원테이블에서 주민등록번호1을 날짜로 치환한 후 500일을 더한 날짜를 검색!
    --(Alias는 회원명, 주민등록번호1, 치환날짜)
    select mem_name 회원명, mem_regno1 주민등록번호1,
       convert(datetime,'19'+mem_regno1) + 500 치환날짜
       from member
    --회원테이블에서 주민등록번호1을 날짜로 치환한 후 '1974-01-01'부터 '1975-12-31'사이의 날짜를 검색!
    --(Alias 회원명, 주민등록번호1, 치환날짜)
    select mem_name 회원명, mem_regno1 주민등록번호1,
       convert(datetime,'19'+mem_regno1) 치환날짜
       from member
       where convert(datetime,'19'+mem_regno1)between'1974-01-01'and'1975-12-31'
    --회원테이블에서 생일을 문자로 치환한 후 LIKE '1975%'의 조건을 사용하여 해당회원을 검색!
    --Alias는 회원명, 생일)
    select mem_name 회원명, mem_bir 생일
       from member
       where convert(char, mem_bir, 121) like '1975%'


    <INPUT style="BORDER-TOP-WIDTH: 1px; PADDING-LEFT: 33px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 1px; FONT-SIZE: 11pt; BORDER-LEFT-COLOR: white; BACKGROUND: #d4d4d4; BORDER-BOTTOM-WIDTH: 1px; BORDER-BOTTOM-COLOR: white; WIDTH: 770px; BORDER-TOP-COLOR: white; PADDING-TOP: 7px; HEIGHT: 30px; BORDER-RIGHT-WIDTH: 1px; BORDER-RIGHT-COLOR: white" value="MSSQL 함수 (기타함수)">

    --******************************************************************
    --      함수(NULL)
    --******************************************************************
    /* ## 데이터를 처리할 때 NULL값의 사용은 최대한 줄여야 하지만 사용해야 할 경우가 있다.
    ## 학생에 대한 정보를 입력할 때 '전화번호' 속성은 전화번호가 없는 학생이 있을 수가 있다 .이런 경우에는 NULL값을 사용한다.
    ## NULL값은 0.1과 같은 특정한 값이 아니고 아무 것도 없는 것을 뜻한다.
    ## SQL에서 NULL값을 허용하지 않는 속성에 대해 NULL값으로 수정하려 한다면 에러가 발생한다. */

    --null값을 찾을 때에는 is null
    select buyer_name buyer_charger
       from buyer
       where buyer_charger is null
    --null인 값을 찾을 때 '='은 성립되지 않는다.
    select buyer_name buyer_charger
       from buyer
       where buyer_charger=null
    --
    select buyer_name buyer_charger
       from buyer
       where buyer_charger =''
    --null값이 아닌 값을 찾으려 할 때 is not null
    select buyer_name buyer_charger
       from buyer
       where buyer_charger is not null
    --ISNULL(c,d) / c가 NULL값이면 d값으로 치환
    --null값일 때 '이름없다' 로 출력!
    select buyer_name, buyer_charger, isnull(buyer_charger, '이름없다')
       from buyer
    --null에다 100을 더하면 null이다.
    select null + 100
    --
    select isnull(null,0)+100
    --
    select mem_name, mem_mileage, mem_mileage+100
       from member
    --
    select mem_name, mem_mileage, mem_mileage + 100
       from member
       where mem_name like '[바-빟]%'
    --
    update member set mem_mileage = null
       where mem_name like '[바-빟]%'
    --
    select mem_name, mem_mileage, isnull(mem_mileage,0) + 100
       from member

    --NULLIF(c,d) / c와 d를 비교하여 같으면 NULL을 다르면 c값을 돌려준다.
    --반환값 : NULL
    select nullif(123,123)
    --반환값 : NULL
    select nullif(1234,'1234')
    --반환값 : 123
    select nullif(123,1234)
    --반환값 : a
    select nullif('a','b')

    --**************************************************
    --예제)
    --**************************************************
    --거래처테이블에서 거래처명, 담당자 조회!
    select buyer_name 거래처,buyer_charger 담당자
       from buyer
    --거래처 담당자 성씨가 '김'이면 null로 갱신! 하기전에 먼저 확인!
    select buyer_name, buyer_charger
       from buyer
       where buyer_charger like '김%'
    --거래처 담당자 성씨가 '김'이면 null로 갱신!
    update buyer set buyer_charger=null
       where buyer_charger like '김%'
    --거래처 담당자 성씨가 '성'이면 SPACE로 갱신! 하기전에 먼저 확인!
    select buyer_name, buyer_charger
       from buyer
       where buyer_charger like '성%'
    --거래처 담당자 성씨가 '성'이면 SPACE로 갱신!
    update buyer set buyer_charger=''
       where buyer_charger like '성%'
    --***********************************************************************
    --      함수 (NULL 관련)
    --***********************************************************************
    --is null, is not null /null값인지 아닌지 비교
    --isnull(c,d)/c가 null값이면 d값으로 치환!
    --nullif(c,d)/c와 d를 비교하여 같으면 null을, 다르면 c값을 돌려준다.
    --***********************************************************************
    --해당 컬럼이 null값 비교 조회
    --1. null이 존재하는 상태로 조회
    select buyer_name 거래처, buyer_charger 담당자
       from buyer
    --2. null을 이용 null값 비교
    select buyer_name 거래처, buyer_charger 담당자
       from buyer
       where buyer_charger = null      --▷' = null ' 대신 ' is null '을 사용해야 제대로 된 구문 !

    --***********************************************************************
    --      함수(GROUP)
    --***********************************************************************
    --AVG(columm)/조회범위 내에 해당 컬럼들의 평균값
    --                     /DISTINCT : 중복된 값은 제외
    --                     /ALL : Default로써 모든 값을 포함(all을 쓰지 않아도 Default값으로 적용)
    --                     /Column명 : NULL값은 제외
    --                     /* : NULL값도 포함(COUNT함수만 사용)
    --                     isnull을 사용하여 NUll값은 '0'등으로 나오게 한다.
    --
    select avg(distinct prod_cost),avg(all prod_cost),
       avg(prod_cost) 매입가평균
       from prod
    --상품테이블의 상품분류별 매입가격 평균 값
    /* (집게함수 앞에 있는 것은 group by에 포함되어있어야만 한다.
          그러나, group by에 있는 것이 집게함수나 그 앞에 포함되지 않아도 무관!) */
    select prod_lgu, avg(prod_cost)'상품분류별 매입가격 평균'
       from prod
       group by prod_lgu
    --상품테이블의 총 판매가격 평균값을 구하시요?
    --(Alias는 상품총판매가격평균)
    select prod_lgu, avg(prod_sale)'상품총판매가격평균'
       from prod
       group by prod_lgu
    --상품테이블의 상품분류별 판매가격 평균값을 구하시요? (Alias는 상품분류, 상품분류별판매가평균)
    select prod_name'상품분류', avg(prod_sale)'상품분류별판매가평균'
       from prod
       group by prod_name

    --**************************************************************************
    --      함수(GROUP)
    --**************************************************************************
    --COUNT(col) / 조회 범위내 해당컬럼들의 자료수
    --COUNT(*) / 선택된 자료의 수
    --      NULL값까지 포함해서 갯수를 헤아린다.
    --
    select count(distinct prod_cost), count(all prod_cost),
       count(prod_cost),count(*)
      from prod
    --상품테이블의 자료수
    select count(*) result1, count(prod_lgu) result2
      from prod
    --상품테이블의 상품분류별 자료수
    select prod_lgu, count(*)'상품분류별 자료의 수'
      from prod
    group by prod_lgu
    --1.거래처테이블의 담당자를 컬럼으로 하여 count집게
    --  (Alias는 "자료수(Distinct)", 자료수, 자료수(*))
    --방식1
    select count(distinct buyer_charger) "자료수(Distinct)",
       count(buyer_charger) "자료수",
       count(*) "자료수(*)"
      from buyer
    --방식2
    select count(distinct buyer_charger) "자료수(Distinct)",
       count(all buyer_charger) "자료수",
       count(*) "자료수(*)"
      from buyer
    --2. 회원테이블의 취미종류를 count집계
    --   (Alias는 취미종류)
    select distinct mem_like "취미종류" from member
    --3. 회원테이블의 취미별 COUNT집계 (Alias는 취미,자료수,자료수(*))
    select mem_like 취미, count(mem_like) "자료수", count(*) "자료수(*)"
       from member group by mem_like
    --4. 회원테이블의 직업종류수를 COUNT집계(Alias는 직업종류수)
    select count(distinct mem_job) "직업종류수"
       from member
    --5. 회원테이블의 직업종류를 집계
    select distinct mem_job "직업종류" from member
    --6. 회원테이블의 직업별 카운트 집계
    select mem_job "직업", count(mem_job) "자료수", count(*) "자료수(*)"
       from member group by mem_job
    --7. 장바구니 테이블의 회원별 카운트 집계
    select cart_member, count(*), count(cart_member), count(distinct cart_member)
       from cart
      group by cart_member

    --**************************************************************************
    --      함수(GROUP)
    --**************************************************************************
    --MAX(col)     / 조회범위 내 해당컬럼들 중 최대값
    --MIN(col)     /조회범위 내 해당컬럼들 중 최소값
    -- 어차피 중복을 배제하나 않하나 최대값과 최소값은 같으므로 distinct를 쓰나 마나이다!
    select max(distinct prod_cost), max(prod_cost),
       min(distinct prod_cost), min(prod_cost)
      from prod
    --상품중 최고판매가겨과 최저판매가격
    select max(prod_sale) 최고판매가,
       min(prod_sale) 최저판매가
      from prod
    --상품중 거래처별 최고매입가격과 최저매입가격
    select prod_buyer 거래처,
       max(prod_cost) 최고매입가,
       min(prod_cost) 최저매입가
      from prod
      group by prod_buyer
    --문제)
    --1. 장바구니 테이블의 회원별 최대구매수량을 검색
    --   (Alias는 회워ID, 최대수량, 최소수량)
    select cart_member 회원ID,
       max(cart_qty) 최대수량,
       min(cart_qty) 최소수량
      from cart
    group by cart_member
    --2. 오늘이 2002년도 5월 15일이라 가정하고 장바구니 테이블에 발생될 추가주문번호를 검색?
    --   (Alais는 최고치주문번호, 추가주문번호)
    -- 우선, cart의 내용을 확인한다.
    select * from cart
    -- 그다음은, 2002년도 5월 15일을 출력하기 위해...
    select * from cart where cart_no like '20020515%'
    --2002년도 5월 15일의 최고치주문번호를 검색
    select max(cart_no) from cart where cart_no like '20020515%'
    --*************************** 중 요 *********************************
    --2002년도 5월 15일의 최고치주문번호와 추가주문번호를 모두 검색!
    --정수형의 범위가 정해져 있기 때문에 convert함수를 썼다.
    select max(cart_no) 최고치주문번호, convert(decimal(13),max(cart_no)) + 1 추가주문번호
      from cart where cart_no like '20020515%'

    --**************************************************************************
    --      함수(GROUP)
    --**************************************************************************
    --SUM(column) / 조회범위 내 해당컬럼들의 합계
    --상품테이블의 매입가의 총합계 값
    select sum(distinct prod_cost), sum(prod_cost)
       from prod
    --상품테이블의 판매가의 총합계 값
    select sum(prod_sale)'상품 판매가 총합계'
       from prod
    --상품테이블의 상품분류별 판매가 합계값
    select prod_lgu, sum(prod_sale)'상품 분류별 판매가 합계'
       from prod
       group by prod_lgu
    --상품입고테이블의 상품별 입고수량의 합계값
    select buy_prod 상품, sum(buy_qty)'입고수량합계'
       from buyprod
       group by buy_prod
    --문제)
    --1. 장바구니테이블의 상품분류별 판매수량의 합계값
    --   (Alias는 상품, 판매수량합계)
    -- 장바구니테이블 모두 출력
    select * from cart
    --판매수량합계
    select left(cart_prod,4) 상품, sum(cart_qty) 판매수량합계
       from cart
      group by left(cart_prod,4)
    --2. 회원테이블의 회원전체의 마일리지 평균, 마일리지 합계, 최고마일리지, 최소마일리지, 인원수를 검색
    --   (Alias는 마일리지평균, 마일리지합계, 최고마일리지, 최소마일리지, 인원수)
    select avg(mem_mileage) 마일리지평균, sum(mem_mileage) 마일리지합계,
       max(mem_mileage) 최고마일리지, min(mem_mileage) 최소마일리지,
       count(*) 인원수 from member

    --**************************************************************************
    --      함수(소 GROUP 분리)
    --**************************************************************************
    --소 GROUP / 집계함수를 제외한 select절에 기술된 column명들은 모두 group by절에 기술!
    --       / group by절에 기술된 column명들은 select절에 기술되지 않아도 무방
    --       / 하지만 결과를 파악하기 위해서는 select절에 기술해주는 것이 타당
    --       / group by절을 기술하면 group by 절에 기술된 column값으로 1개의 table이 소group으로 나눠진다.
                 결과는 column값으로 sort되어서 출력된다.
    --상품테이블에서 거래처, 상품분류별로 최고판매가, 최소판매가, 자료수를 검색
    select * from prod
    --
    select prod_buyer 거래처, prod_lgu 상품분류,
       max(prod_sale) 최고판매가,
       min(prod_sale) 최소판매가,
       count(prod_sale) 자료수
       from prod
      group by prod_buyer, prod_lgu
    --
    --1. 장바구니테이블에서 회원, 상품분류별로 구매수량평균, 구매수량합계, 자료수를 검색?
    --   (Alias는 회원ID,상품분류,구매수량평균,구매수량합계,자료수)
    --   (회원ID, 상품분류 순으로 sort하시요)
    select * from cart
    --
    select cart_member 회원ID, left(cart_prod,4) 상품분류,
       avg(cart_qty) 구매수량평균,
       sum(cart_qty) 구매수량합계, count(cart_qty) 자료수
      from cart
       group by cart_member, left(cart_prod,4)
       order by cart_member, left(cart_prod,4)
    --
    --2. 회원테이블에서 지역(주소1의 2자리),생일년도별로 마일리지평균,마일리지합계, 최고마일리지,최소마일리지,자료수를 검색?
    --   (Alias는 지역,생일연도,마일리지평균,마일리지합계,최고마일리지,최소마일리지,자료수)
    select * from member
    --
    select left(mem_add1,2) 지역, year(mem_bir) 생일연도,
       avg(mem_mileage) 평균, sum(mem_mileage) 합계,
       max(mem_mileage) 최대, min(mem_mileage) 최소,
       count(*) 자료수
      from member
     group by left(mem_add1,2), year(mem_bir)

    --*************************************************************************
    --      함수(system)
    --*************************************************************************
    --ISDATE(c) / 타당한 날짜 포맷인지 확인 : 날짜면 1, 아니면 0
    --ISNUMERIC(n) / 타당한 숫자포맷인지 확인 : 숫자면 1, 아니면 0
    --CASE WHEN / 연속적인 조건문(자주활용되는 함수)
    --                     CASE WHEN ~ THEN ~ ELSE ~ END
    --
    select isdate('20000101') result1,
       isdate('12345678') result2,
       isdate('abc') result3
    --
    select isnumeric(1234.5678) result1,
       isnumeric('1234.5678') result2,
       isnumeric('ABCDEFG') result3
    --
    select case when'나'='나' then'맞다'
       else'아니다' end result
    --
    select case'나'when'철호'then'아니다'
       when'너' then'아니다'
       when'나' then'맞다'
       else'모르겠다' end result
    --
    select prod_name 상품, prod_lgu 분류,
       상품분류=
       case when prod_lgu = 'p101' then '컴퓨터제품'
          when prod_lgu = 'p102' then '전자제품'
          when prod_lgu = 'p201' then '여성케주얼'
          when prod_lgu = 'p202' then '남성케주얼'
          when prod_lgu = 'p301' then '피혁잡화'
          when prod_lgu = 'p302' then '화장품'
          when prod_lgu = 'p401' then '음반/CD'
          when prod_lgu = 'p402' then '도서'
          when prod_lgu = 'p403' then '문구류'
        else '미등록분류'
       end
      from prod

    --10만원 초과 상품판매가 가격대를 검색
    select * from prod
    --
    select prod_name 상품, prod_price 판매가,
        case
          when(100000-prod_price)>0 then'10만원미만'
          when(200000-prod_price)>0 then'10만원대'
          when(300000-prod_price)>0 then'20만원대'
          when(400000-prod_price)>0 then'30만원대'
          when(500000-prod_price)>0 then'40만원대'
          when(600000-prod_price)>0 then'50만원대'
          when(700000-prod_price)>0 then'60만원대'
          when(800000-prod_price)>0 then'70만원대'
          when(900000-prod_price)>0 then'80만원대'
          when(1000000-prod_price)>0 then'90만원대'
        else'100만원이상'
       end'가격대'
      from prod
     where prod_price>100000
    --문제)
    --회원정보테이블의 주민등록 뒷자리(7자리 중 첫째자리)에서 성별 구분을 검색
    --  (Alias는 회원명, 주민등록번호(주민1-주민),성별)
    select * from member
    --
    select (mem_regno2)
      from member
    --첫번 째 방법
    select mem_name 회원명, mem_regno1+'-'+mem_regno2 주민등록번호,
      case when left(mem_regno2,1)=1 then'남자'
        when left(mem_regno2,1)=2 then'여자'
        else'알수없는성별'
      end'성별'
     from member
    --또 다른 방법
    select mem_name 회원명, mem_regno1+'-'+mem_regno2 주민등록번호,
      case left(mem_regno2,1) when '1' then '남자'
        when '2' then '여자'
       else'알수없는성별'
      end'성별'
     from member

    출처 : Tong - ahnz님의 데이터베이스통

    + Recent posts