데브피아에서 펀 글인뎅.. 좋은 내용입니다.

이곳 Tip&Tech의 글 중에 게시판의 속도를 높이자.. 라는 글에서 사용한 방법이
바로이 파생 테이블이기도 합니다.

/*
-----------------------------------------------------------------------
    소개
-----------------------------------------------------------------------

+ 파생 테이블이란?
우선, 파생 테이블에 대해서 간단히 소개를 하겠습니다.
파생 테이블(Derived Table, 인라인 뷰라고도 하죠)
T-SQL에서 FROM절에는 일반적으로 Table_Source 형식의 데이터 집합이 올
수 있죠.

다음은 테이블 소스 형식의 예입니다.
- Table
- View
- Rowset 함수
- OPENXML 함수
- Derived Table(파생 테이블)

위에서 보는 것처럼, 테이블 소스가 될 수 있는 형식은 다양합니다.
그러나, SQL을 전문적으로 배우기 전에는 FROM절 하면 항상 테이블 또는 뷰 정도만을
생각하게 됩니다.

이번엔 파생 테이블에 대한 기본적인 사용법과 함께, 어떤 경우에 파생 테이블을
적용할 수 있는지, 또한 다른 방법들과 어떤 차이가 있는지 다양한 상황을 통해
살펴 보도록 하겠습니다.

+ 집합 개념의 시작
이번 강좌를 진행하면서, 제 개인적인 목적은 Advanced T-SQL에 대한 내용을
전반적으로 살펴보면서 집합적 개념을 인식할 수 있도록 유도하는 것입니다.
이전 강좌를 통해서도 집합 개념에 대한 중요성을 계속 언급을 드리고 있습니다.
파생 테이블 역시, 제대로 사용하기 위해서 그리고 그 이점을 최대한 활용하기 위해선
집합적 개념의 이해와 접급 시도를 필요로 합니다.

특히 두 개 이상의 테이블을 조인하거나, 다른 집합에서 파생된 데이터를 가공하거나
다른 집합을 기준으로 새로운 집합을 산출하는 처리를 하는 경우 파생 테이블의 사용
여부는 상당한 차이를 보여줍니다.

여러분들은, 집합 개념의 이해가 어떤 내용인지를 이 강좌의 마지막 예제를 통해서
느끼실 수가 있으실 겁니다.


+ 서브 쿼리, 임시 테이블의 대체
파생 테이블을 적용할 수 있는 또 다른 사례가 바로 서브 쿼리나 임시 테이블을
사용하는 쿼리입니다.
SQL의 정답은 없다라는 말을 곧 잘 합니다. 하나의 결과를 산출하기 위해서 시도할
수 있는 방법은 여러가지가 있으며, 하나의 방법이 좋은 결과를 보였다고 해서 다른
SQL에서도 동일하다는 보장은 없다는 뜻 입니다.
서브 쿼리, 임시 테이블(또는 2000의 Table 데이터형) 그리고 파생 테이블 이러한
방법 중 가장 좋은 결과(성능 및 비용 측면)를 가지는 방법을 선택하는 시도가
필요합니다.


-----------------------------------------------------------------------
    준비 사항
-----------------------------------------------------------------------
자, 그럼 강좌를 보기 전에 기본적인 준비가 있어야 겠죠.

쿼리 분석기에서 윈도우를 하나 오픈하시죠(Ctrl-N),
그리고 엔터프라이즈 관리자에서 Northwind 데이터베이스 다이어그램을 하나
작성하시면 좋겠습니다.

제공되는 예제는 기본적으로 Northwind DB를 사용할 것입니다.


-----------------------------------------------------------------------
    파생 테이블의 미리보기 예제
-----------------------------------------------------------------------
우선, 간단한 예제를 먼저 보도록 하겠습니다.

*/
USE Northwind
GO

SELECT p.productid, p.quantity
FROM (SELECT productid, sum(quantity) quantity
FROM dbo.[order details] od
GROUP BY productid) as p



/*
-----------------------------------------------------------------------
    파생 테이블 작성 규칙
-----------------------------------------------------------------------
1. ANSI-92 SQL 표준.
2. ()안에 SELECT문을 기술.
3. 반드시 테이블 별칭(Alias)을 기술합니다.


    -- 테이블 별칭을 생략한 경우
    SELECT p.productid, p.quantity
    FROM (SELECT productid, sum(quantity) quantity
            FROM dbo.[order details] od
            GROUP BY productid)

    서버: 메시지 170, 수준 15, 상태 1, 줄 4
    줄 4: ')' 근처의 구문이 잘못되었습니다.

파생 테이블의 ANSI-92 표준이기 때문에 다른 DBMS에서도 동일하게 적용하실 수 있는
기능입니다. SQL Server는 6.5버전부터 지원이 됩니다.

작성 방법은 어렵지 않을 것입니다.
쉽게 생각하셔서, SELECT문과 별칭을 가진 테이블 소스라고 생각하시면 되죠.

기본적인 사항을 배우셨으니, 이제 실제 다양한 사례를 통해서 파생 테이블을 어떤
경우에 적용할 수 있는지 보도록 하겠습니다.

그냥 읽어보지만 마시고, 반드시 실행과 테스트를 해 보시기 바랍니다.
*/


/*
-----------------------------------------------------------------------
    파생 테이블 적용 사례
-----------------------------------------------------------------------
1. 파생 테이블과 조인하기

예제. Products 테이블에서 제품별 단가를 산출하되,
같은 종류 별(categoryid) 평균단가(avgprice) 를 함께 출력한다.


참조테이블.
    Products

출력양식.
    productid unitprice avgprice
    ----------- --------------------- ---------------------
    1 18.0000 42.3708
    2 19.0000 42.3708
    3 10.0000 23.0625
    ...

*/
USE northwind

-- 각 제품의 단가 출력
SELECT p.productid, p.unitprice
    FROM dbo.products as p

-- 같은 종류별로 평균 단가 출력
SELECT avg(unitprice) as avgprice
    FROM dbo.products
    GROUP BY categoryid


-- 1) 서브 쿼리를 이용한 최종 결과
SELECT p.productid, p.unitprice,
    (SELECT avg(unitprice)
        FROM dbo.products a
        WHERE a.categoryid = p.categoryid
        GROUP BY a.categoryid) as avgprice
   
FROM dbo.products as p


-- 2) 파생 테이블을 이용한 최종 결롸
SELECT p.productid, p.unitprice, a.avgprice
FROM dbo.products p JOIN
(SELECT categoryid, avg(unitprice) as avgprice
    FROM dbo.products
    GROUP BY categoryid) AS a    ON a.categoryid = p.categoryid
ORDER BY p.productid



/*
2. (GROUP BY 절과 함께 UPDATE하기)

예제. 매출액( sum(unitprice) ) 5위 까지의 제품의 단가를 20% 상향 조정
하기 위한 update 작성.

임시 테이블을 작성할까?


참조테이블.
    [Order Details]

출력양식.
    없음.

*/

-- 매출액 5위까지 산출
SELECT TOP 5 productid, sum(unitprice) as unitprice
FROM dbo.[order details]
GROUP BY productid
ORDER BY unitprice DESC


-- 매출액 1위인 38번 제품의 현재 단가: 263.5 (확인용)
-- (여러분들의 금액을 적어두시면 됩니다)
SELECT productid, unitprice
FROM dbo.products
WHERE productid = 38


-- 파생 테이블을 이용한 경우, 316.2로 변경(38번 제품)
UPDATE p SET p.unitprice = p.unitprice * 1.2
FROM dbo.products as p JOIN
    (SELECT TOP 5 productid, sum(unitprice) as unitprice
            FROM dbo.[order details]
            GROUP BY productid
            ORDER BY unitprice DESC) as a
    ON a.productid = p.productid


/*
3. 재미있는 예제.

(통계치에 대한 분포 데이터 출력)

예제. 고객별 주문횟수에 대한 분포 데이터 출력
*/
SELECT customerid, count(*) as ordercnt
    FROM dbo.orders
    GROUP BY customerid
    ORDER BY ordercnt


-- 파생 테이블을 이용한 경우, 재미있죠?
SELECT cnt, Replicate('=', count(*)) as freq
FROM (SELECT customerid, count(*) as cnt
        FROM dbo.orders
        GROUP BY customerid) as c
GROUP BY cnt



/*
문제 유형.
    1:M관계의 조인후 1 관계 산출하는 SQL

파생 테이블을 적용해서 성능 향상을 유도할 수 있는 일반적인 사례가 바로
GROUP BY 절이 사용되는 경우입니다.

GROUP BY 절은 M쪽 관계의 집합을 1쪽 관계로 산출하는 집합 연산입니다.
특히 조인과 함께 적용되는 경우 JOIN과 GROUUP BY 이 두가지 집합 연산의 처리
순서는 성능에 있어서 중요한 요소가 됩니다.

제 말의 의미를 아시겠는지?
아래 예제를 살펴보시고, 실제 성능 평가는 마지막에서 언급합니다.

요구하는 출력양식을 위한 SQL을 직접 작성해 보십시오.
그리고 파생 테이블을 사용한 경우와 비교를 해 보시기 바랍니다.


참조테이블.
    Orders, [Order Details], Products

출력사항.
    1996-12-31 이전에 주문된 제품별 단가의 합계 출력

출력양식.
    -------------------------------
    제품명 단가합계
    -------------------------------
    Alice Mutton 249.6000
    Aniseed Syrup 8.0000   
    ...

SQL 구현 방법
    1. 일반 조인을 이용한 경우
    2. 파생 테이블을 이용한 경우

*/
-- 1. 조인을 이용한 경우
-- 대부분의 개발자들이 이렇게 처리를 하십시다.
SELECT productname, sum(od.unitprice) sumprice
FROM dbo.[order details] od join dbo.orders o
    ON o.orderid = od.orderid join dbo.products p
    ON od.productid = p.productid
WHERE orderdate < '19961231'
GROUP BY productname



-- 2. 파생 테이블을 이용한 경우, 비교를 해 보시죠.
SELECT productname, sumprice
FROM dbo.products p
    JOIN (SELECT productid, sum(od.unitprice) sumprice
        FROM dbo.[order details] od join dbo.orders o
         ON od.orderid = o.orderid
        WHERE orderdate < '19961231'
        GROUP BY productid) od
ON od.productid = p.productid
ORDER BY productname


/*
성능 비교   

아래 세션 옵션(STATISTICS IO)을 ON으로 설정하신 뒤에 위의
두 명령을 쿼리 분석기에서 다시 실행하고 나면 아래와 같은 결과를 추가적으로
보여줍니다.
만일, 쿼리 분석기에서 실행 계획을 보고 평가하실 수 있는 분이라면 실행
계획도 함께 확인하시면 훨씬 도움이 되실겁니다.(Ctrl-K)

STATISTICS IO 설명
이 옵션을 ON으로 설정한 뒤, SQL을 실행하시면 쿼리 분석기의 결과 창에
실행 SQL에 의해 발생된 각 테이블의 페이지(데이터 페이지&인덱스 페잊) IO 횟수,
테이블 스캔 횟수 및 기타 정보를 보실 수가 있습니다.

SQL 성능 측정을 위한 기본적인 방법입니다.

자, 그럼 비교를 해 보시죠.
*/
SET STATISTICS IO ON

-- 1. 조인을 이용한 SQL을 실행한 경우의, 디스크 처리 통계 정보

-- 'Products' 테이블. 스캔 수 401, 논리적 읽기 수 802, 물리적 읽기 수 0, 미리 읽
기 수 0.
-- 'Order Details' 테이블. 스캔 수 151, 논리적 읽기 수 303, 물리적 읽기 수 0, 미
리 읽기 수 0.
-- 'Orders' 테이블. 스캔 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0.


-- 2. 파생 테이블을 이용한 SQL을 실행 한 경우의, 디스크 처리 통계 정보

-- 'Products' 테이블. 스캔 수 74, 논리적 읽기 수 148, 물리적 읽기 수 0, 미리 읽기
수 0.
-- 'Order Details' 테이블. 스캔 수 151, 논리적 읽기 수 303, 물리적 읽기 수 0, 미
리 읽기 수 0.
-- 'Orders' 테이블. 스캔 수 1, 논리적 읽기 수 2, 물리적 읽기 수 0, 미리 읽기 수 0.


/*
2가지 IO 비교에서
'Products' 테이블에 대한 논리적 읽기 수를 비교해 보십시오.

1. 스캔 수:401, 논리적 읽기 수:802
2. 스캔 수:74, 논리적 읽기 수:148

상당한 차이의 Page IO를 보실 수가 있습니다.
예제에서 사용된 테이블의 레코드 수는 각각 Products(77), Orders(830),
Order Details(2155) 건 밖에 되지 않습니다.

대량의 데이터를 가진 테이블의 경우를 상상해 보십시오.


-----------------------------------------------------------------------
    Horizontal Partitioning(=Partitioned View)
-----------------------------------------------------------------------
SQL Server 2000에 새로 추가된 Enterprise 기능 중에 한 가지가 바로
Distributed Partitioned View(분산 분할 뷰) 기능입니다.
데이터 베이스 층에서 데이터 로드 밸런싱을 통한 확장성을 구현할 수 있는
기능입니다.

이 분할 뷰를 파생 테이블을 형식으로도 구성을 하실 수가 있습니다.
분산 분할 뷰에 대해서는 기회가 닿는 대로 소개를 하도록 하겠습니다.


-----------------------------------------------------------------------
    Inline Table-value 함수도 동일한 기능을 수행
-----------------------------------------------------------------------
SQL Server 2000에서 또 다른 추가 기능이 바로 UDF(사용자 정의 함수) 기능이죠.
UDF는 세 가지 형식으로 작성하실 수가 있습니다.

1. Scalar 함수
2. Table-value 함수 중
    2-1) Inline
    2-2) Multi Statement

입니다.
이 중 Inline Table-value 형식의 함수는
일반적인 뷰, 파생테이블과 동일하게 처리 방법으로 사용이 됩니다.


-----------------------------------------------------------------------
    마무리.
-----------------------------------------------------------------------
파생 테이블에 대한 사례는 많이 있습니다.
너무 부담을 드릴 수는 없는 관계로 몇 가지 사례만을 소개 했습니다.
집합 개념의 대한 정립. 파생 테이블 활용에서부터 시작하십시오.
변환된 모습을 느끼실 수 있으실 겁니다.


이번 강좌도 여러분들에게 많은 도움이 되셨으면 하는 바램을 가져 봅니다.
벌써 새벽 4시군요, 오늘은 토요일, 즐거운 주말 되십시오.