출처 : http://www.sqlleader.com/mboard.asp?exec=view&strBoardID=SS2005TSQL&intPage=1&intCategory=0&strSearchCategory=|s_name|s_subject|&strSearchWord=&intSeq=972



자주가는 블로그에 재미있는 꺼리가 올라와서 간단히 정리해봤습니다.
 
 
다음과 같은 형태의 데이터를


다음과 같이 같은 id 그룹별로 쉼표(,)로 붙여서 출력하는 문제입니다.
 
조건은 
  a. 커서를 쓰면 안되며,
  b. 임시 테이블 또는 테이블 변수와 같은 것 사용 없이 쿼리 한 방으로 결과 뽑기
  c. 대신 SQL 2005의 CTE 등은 이용해도 되기~ 입니다.
 
아래의 여러 고수들이 제시한 방법들을 보시기 전에 먼저 한 번 고민해 보시길 바랍니다.^^
 
 
테스트용 데이터 생성하기
 

USE TEMPDB

GO

 

 

IF EXISTS (SELECT * FROM SYS.TABLES WHERE NAME = 't1' AND type = 'U')

        DROP TABLE t1

Go

 

 

CREATE TABLE t1 (id INT, NAME VARCHAR(MAX))

INSERT t1 values (1,'Jamie')

INSERT t1 values (1,'Joe')

INSERT t1 values (1,'John')

INSERT t1 values (2,'Sai')

INSERT t1 values (2,'Sam')

INSERT t1 values (3,'Roger')

INSERT t1 values (4,'Walter')

INSERT t1 values (4,'Teddy')

GO

 

SELECT * FROM T1

GO

/*

id      NAME

-----------------

1       Jamie

1       Joe

1       John

2       Sai

2       Sam

3       Roger

4       Walter

4       Teddy

*/

 
 
 
 
[방법 1]

--------------------------------------------------------------------------------------------

-- Nick Barclay

--------------------------------------------------------------------------------------------

WITH ConcatNamesCTE (id, [NAME], rn)

AS

(

        SELECT id, [NAME], rn

        FROM

        (

               SELECT id, [NAME],

                       row_number() OVER(PARTITION BY id ORDER BY id) AS rn

               FROM t1

        ) a

        WHERE rn = 1

        UNION ALL

        SELECT b.id, cn.[NAME] + ',' + b.[NAME], b.rn

        FROM

        (

               SELECT id, [NAME],

                       row_number() OVER(PARTITION BY id ORDER BY id) AS rn

               FROM t1

        ) b

        INNER JOIN ConcatNamesCTE cn ON cn.id = b.id AND cn.rn + 1 = b.rn

)

SELECT d.id, d.[NAME]

FROM

(

        SELECT MAX(rn) AS rn, id

        FROM ConcatNamesCTE

        GROUP BY id

) c

INNER JOIN ConcatNamesCTE d ON d.id = c.id AND d.rn = c.rn

ORDER BY id

GO

 
 
[방법 2]

--------------------------------------------------------------------------------------------

--Adrian Downes

--------------------------------------------------------------------------------------------

SELECT res.id, MAX(res.[NAME]) AS [NAME]

FROM

(

        SELECT c.id,

        CASE

               WHEN PATINDEX('%' + d.[NAME] + '%', c.[NAME]) = 0 AND c.id = d.id

                       THEN c.[NAME] + ', ' + d.[NAME]

               ELSE c.[NAME]

        END AS [NAME]

        FROM

        (

               SELECT a.id, MIN(a.[NAME]) AS [NAME]

               FROM

               (

                       SELECT y.id,

                       CASE

                               WHEN PATINDEX('%' + z.[NAME] + '%', y.[NAME]) = 0 AND y.id = z.id

                              THEN y.[NAME] + ', ' + z.[NAME]

                              END AS [NAME]

                       FROM t1 y

                       INNER JOIN t1 z ON y.id = z.id

               ) a

               GROUP BY a.id

        ) c

        INNER JOIN t1 d ON c.id = d.id

) res

GROUP BY res.id

GO

 
 
[방법 3]

--------------------------------------------------------------------------------------------

--Jamie Hunter

--------------------------------------------------------------------------------------------

SELECT

        DISTINCT

        id

        , STUFF(

               (SELECT ',' + name AS [text()] FROM t1 b WHERE b.id = a.id FOR XML PATH(''))

               ,  1,  1,  ''

               ) AS name_csv

FROM t1 a

ORDER BY 1

GO

 
 
[방법 4]

--------------------------------------------------------------------------------------------

--Rick R

--------------------------------------------------------------------------------------------

WITH t2 AS (

        SELECT id, MIN(name) name

        FROM t1 GROUP BY id

        UNION ALL

        SELECT a.id, a.[NAME] + ',' + b.[NAME] AS name

        FROM t1 a JOIN t2 b ON a.id = b.id AND a.[NAME] > b.[NAME]

)

SELECT id, MAX(name) FROM t2

GROUP BY id

GO

 
 
 
개인적으로는 방법 3이 좋게 보이네요..^^ 여러분은 어떤 방법이 제일 좋으신지요..
더 좋은 방법 있으면 리플 달아주세요.
 
 
select  servername, batch_id, count(*) cnt from f_pgm_info
where   biz_dt between cast(convert(varchar,getdate(),111)+' 00:00:00:000' as datetime)
                   and cast(convert(varchar,getdate(),111)+' 23:59:59:998' as datetime)
and     batch_id like '%.BATCH'
group by servername, batch_id
order by servername, batch_id
;
ebroker.2035        FO.BATCH    5
ebroker.2035        JU.BATCH    2
ebroker.2035        KP200.BATCH 1
ebroker.2035        LP.BATCH    2
ebroker.2035        MC.BATCH    2
ebroker_save.2076   FO.BATCH    5
ebroker_save.2076   JU.BATCH    2
ebroker_save.2076   KP200.BATCH 1
ebroker_save.2076   LP.BATCH    2
ebroker_save.2076   MC.BATCH    2
etr.2034            FO.BATCH    5
etr.2034            JU.BATCH    2
etr.2034            KP200.BATCH 1
etr.2034            LP.BATCH    2
etr.2034            MC.BATCH    2
etr_ord.2019        FO.BATCH    5
etr_ord.2019        JU.BATCH    2
etr_ord.2019        KP200.BATCH 1
etr_ord.2019        LP.BATCH    2
etr_ord.2019        MC.BATCH    2
etr_sch.2018        FO.BATCH    5
etr_sch.2018        JU.BATCH    2
etr_sch.2018        KP200.BATCH	1
etr_sch.2018        LP.BATCH    2
etr_sch.2018        MC.BATCH    2


select  servername
    ,   sum(case when batch_id = 'JU.BATCH' then cnt end) ju_batch
    ,   sum(case when batch_id = 'FO.BATCH' then cnt end) fo_batch
    ,   sum(case when batch_id = 'KP200.BATCH' then cnt end) kp200_batch
    ,   sum(case when batch_id = 'LP.BATCH' then cnt end) lp_batch
    ,   sum(case when batch_id = 'MC.BATCH' then cnt end) mc_batch
from
(
    select  servername, batch_id, count(*) cnt 
    from    f_pgm_info
    where   biz_dt between cast(convert(varchar,getdate(),111)+' 00:00:00:000' as datetime)
                       and cast(convert(varchar,getdate(),111)+' 23:59:59:998' as datetime)
    and     batch_id like '%.BATCH'
    group by servername, batch_id
) a 
group by servername
order by servername
;

ebroker.2035        2   5   1   2   2
ebroker_save.2076   2   5   1   2   2
etr.2034            2   5   1   2   2
etr_ord.2019        2   5   1   2   2
etr_sch.2018        2   5   1   2   2

출처 : http://sthyun.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%95%A8%EC%88%98%EC%9D%98-BlockingNon-Blocking-IO-%EB%8F%99%EC%9E%91

1. 입력함수: read, readv, recv, recvfrom, recvmsg

Blocking TCP 소켓인 경우, 소켓수신버퍼에 수신된 데이터가 없으면, 프로세스는 sleep한다. 데이터가 도착하면 (그것이 충분한 크기가 아닐지라도) 프로세스는 깨어난다. 원하는 크기만큼 도착할때까지 기다리려면, while로 계속 받아 붙이던가, 아니면, MSG_WAITALL 플래그를 이용한다.

UDP 소켓인 경우, 소켓수신버퍼가 비어 있으면, 프로세스는 sleep한다. UDP패킷이 도착하면, 프로세스는 깨어난다.

Nonblocking 소켓의 경우, 수신버퍼가 비어 있는 경우, 에러 EWOULDBLOCK 으로 바로 리턴한다.

 

2. 출력함수: write, writev, send, sendto, sendmsg

Blocking TCP 소켓의 경우, 출력함수는 어플리케이션의 데이터를 커널의 소켓전송버퍼에 복사한다. 만약 소켓전송버퍼에 공간이 없으면, 프로세스는 sleep한다.

Nonblocking TCP 소켓의 경우, 소켓전송버퍼에 공간이 없는 경우, 에러 EWOULDBLOCK으로 바로 리턴한다. 만약 소켓전송버퍼에 약간의 공간이 있는 경우, 복사가능한 공간을 바이트로 리턴한다.

UDP소켓은 실제로 소켓 전송 버퍼가 없다. 즉, 호출 즉시 UDP/IP스택으로 전달하므로, block되지 않는다.

 

3. accept 함수

Blocking 소켓인 경우, 새로운 연결이 없으면, 프로세스는 sleep한다.

Nonblocking 소켓의 경우, 새로운 연결이 없으면, 에러 EWOULDBLOCK으로 리턴한다.

 

4. connect 함수

Blocking TCP 소켓의 경우, 실제 연결이 될 때까지, 즉 SYN에 대한 ACK을 받을때까지 block되어 있는다.

NonBlocking TCP 소켓의 경우, 에러 EINPROGRESS로 리턴한다. (같은 호스트의 경우에는 바로 정상 연결이 이루어 질 수 있다)

UDP 소켓의 경우 connect는 가상의 연결을 만드는 것이라서 바로 리턴한다.

 

(참고: UNIX Network Programming, vol.1, 2nd edition, Ch.15)

 

5. close()

1. Connect 관련 Test

Ÿ 정상적인 경우의 Connect

내용

정상적으로 Connect가 이루어지는 경우, Connect 호출 후, 접속 완료까지 소요되는 시간

방법

Connect 호출 전, 호출 후의 시간을 측정하여 표시한다.

결과

호출 전 시간과 호출 호의 시간이 동일하게 표시됨

Window의 Timer의 유효치 이내의 시간으로 접속

결과분석

 

Ÿ Server Process가 없는 경우의 Connect

내용

연결할 IP에 대한 Computer가 켜져 있으나, Server Process가 없는 경우, Connect Fail의 형태

방법

Server Process를 실행하지 않고 Connect 시도

결과

1, 2초 후에 Connect Fail 발생

결과분석

연결할 Computer에서 Bind된 Port가 없음을 알 수 있으므로 Timeout은 발생하지 않고, 일정 시간내에 Connect Fail이 발생함

Ÿ 연결대상 Computer가 없는 경우의 Connect

내용

연결할 IP에 대한 Computer가 존재하지 않거나 꺼져 있는 경우, Connect Fail의 형태

방법

없는 IP에 대한 Connect 시도

결과

20여초 후에 Connect Fail 발생

결과분석

Timeout 될 때까지 IP를 찾음

 

2. Send/Receive 관련 Test

Ÿ Network Cable을 제거한 즉시 Send

내용

Network Cable을 제거한 즉시 Send를 하는 경우에 대한 결과

방법

Socket 연결 후, Network Cable을 제거한 즉시 Send 시킴

결과

Send가 성공함

 

[Non Blocking Mode]

Socket Close 메시지가 발생할 때까지 성공

[Blocking Mode]

Socket Close 메시지가 발생할 때까지 첫 Send는 성공, 두번째부터는 실패함

 

연결이 끊긴 것을 감지할 때까지는 성공함

7초 정도가 지난 후, 연결 끊김을 감지함

상대방의 Network Cable을 제거하였을 경우에는 약 3분 정도의 시간이 지난 후, 연결 끊김을 감지함

결과분석

Network Cable을 제거하더라도 한동안 Process는 그것을 인식하지 못함

Send는 TCP/IP 하부 레벨에 데이터 전송이 처리되면 성공으로 간주하는 것으로 판단됨

Ÿ Network Cable을 제거한 즉시 다시 연결 후 Send

내용

Network Cable을 제거한 즉시, 다시 Cable을 연결하여 Send 성공여부를 Test

Receive 측 Data Receive 성공여부도 함께 Test

방법

Network Cable을 제거 후, Close를 감지하기 전에 다시 연결하여 Send, Receive의 성공여부를 Test함

결과

Send, Receive 모두 성공

결과분석

Close를 감지하기 전에 다시 Cable을 연결하면, 연결은 유지됨

Ÿ Network Cable을 제거 후, 다시 연결하는 시간을 변화하여 Send

내용

Network Cable 제거 후, 다시 연결하는 시간을 변화시켜 위의 Test를 반복하여 성공여부를 확인

방법

Network Cable 제거 후, 다시 연결하는 시간을 변화시켜 위의 Test를 반복하여 성공여부를 확인

결과

Close를 감지하기 전에 다시 연결하면 Send, Receive 모두 성공

결과분석

 

Ÿ Send 후, Receive를 호출하지 않은 경우

내용

Data를 Send하고 Receive 측에서 Receive 함수를 호출하지 않는 경우, Timeout 발생여부와 Timeout 발생시간을 확인

방법

Data를 Send하고 Receive측에서 Receive 함수를 호출하지 않음

결과

Timeout이 발생하지 않음

결과분석

 

Ÿ Send, Receive Message 발생 수 소규모 데이터

내용

비교적 작은 크기의 데이터(수십 byte 내외)를 아주 빠르게 Send를 여러 번 호출하는 경우, Receive 측에 Receive Message의 발생과 실제 Receive한 Data의 크기를 확인

- Send 수와 Receive Message 발생 수를 비교검사

- Send한 Data 크기와 Receive한 Data 크기 비교

- Receive 시에 실제 Send한 Data보다 수배의 크기의 Buffer로 Receive한 경우와 Send한 Data의 크기와 동일한 크기의 Buffer로 Receive하는 경우의 비교

방법

100 Byte 크기의 Data를 10번 Send함

결과

Ÿ Receive Buffer 크기가 1024 byte인 경우

-       Receive Message 2번 발생

-       첫번째는 100 byte를, 두번째는 900 byte를 Receive 함

Ÿ Receive Buffer 크기가 100 byte인 경우

-       Receive Message는 10번 발생

-       모두 100 byte 씩 Receive함

결과분석

작은 크기의 데이터를 빠르게 연속적으로 전송하는 경우, Network 하부 Layer에서 몇 개의 데이터를 묶어서 한꺼번에 전송하게 된다. 따라서 Send한 Data와 실제 전송되는 Data 크기는 달라질 수 있으며, Send한 수와 Receive Message의 발생 또한 다를 가능성이 있다.

하지만 두번째 Test 결과로 봤을 때, Socket의 내부적인 Buffer에서 Receive Buffer로 Data를 읽어 들일 때, 내부 Buffer를 모두 비울때까지 Receive Message를 자체적으로 발생시키는 것으로 예상된다.

Ÿ Send, Receive Message 발생 수 대규모 데이터

내용

비교적 큰 크기의 데이터(수십 Kbyte 이상)를 Send 하는 경우, Receive 측에 Receive Message의 발생과 실제 Receive한 Data의 크기를 확인

방법

다양한 크기의 Data를 10번 Send 함

결과

- 50Kbyte 크기의 Data일 경우, Receive Message 33번 발생. 한번에 약 1.7Kbyte 수신

- 10Kbyte 크기의 Data일 경우, Receive Message 14번 발생. 한번에 받는 양이 일정하지 않음

- 100Kbyte 크기의 Data일 경우, Receive Message 63번 발생. 한번에 약 17Kbyte 수신

결과분석

한번에 큰 크기의 데이터를 Send하는 경우, Receive 측에서 Receive Message가 발생한 시점에 모든 데이터가 전송되지 않는다. 따라서 Receive Message 발생 시점에 Receive 함수를 호출하면 호출 시점까지 전송된 데이터만을 읽어들이게 된다. 그 이후 전송되는 데이터들은 별도의 Message가 다시 발생하며, 모든 데이터가 Receive될 때까지 Receive Message가 발생한다.

따라서 대용량 Data를 Receive하는 경우에는 데이터의 길이에 대한 정보를 미리 받아서 그 데이터가 모두 도착할때까지 읽도록 하여야 완전한 데이터를 수신할 수 있다.

Receive Message의 발생은 Receive Buffer의 크기와 한번의 Receive Message에 대해서 호출되는 Receive 함수의 수에 따라 그 발생횟수는 달라질 수 있다.

 


3. Close 관련 Test

Ÿ Close Message 발생 Test

내용

각 상황에 따른 Close Message의 발생여부 검사

-       Process를 비정상적으로 강제 Kill 시킨 경우

-       Computer의 전원이 꺼진 경우

-       Network Cable을 제거한 경우

-       Network Cable을 제거한 후 다시 연결하고 접속을 해제하는 경우

-       Network Cable을 제거한 후 Prcess를 종료하고 다시 Cable을 연결하는 경우

방법

 

결과

-       작업관리자에서 Process를 종료하였을 경우 Close Message 발생함

-       Network Cable을 제거한 경우 자신의 Cable 제거시 7초정도, 상대방 Cable 제거시 3분 정도 후에 Cloase Message 발생

-       Network Cable 제거 후 Close Message가 발생하기 전에 다시 연결하여 접속을 해제하는 경우, 정상적으로 Close Message 발생

-       Network Cable을 제거한 후 Process를 종료하고 다시 Cable을 연결하는 경우, Close Message 발생하지 않음

결과분석

 

 



출처 : http://www.cyberciti.biz/faq/yum-downloadonly-plugin/

----------------------------------------
방법1 : yum-downloadonly plugin 이용
----------------------------------------
[a] --downloadonly : don't update, just download a rpm file
[b] --downloaddir=/path/to/dir : specifies an alternate directory to store packages such as /tmp

1) plugin 설치
# yum install yum-downloadonly

2) 다운로드
# yum install httpd -y --downloadonly
# yum update httpd -y --downloadonly
-> 다운로드 디렉토리 : /var/cache/yum/base/packages
                                /var/cache/yum/updates/packages

3) 지정한 디렉토리로 패키지 다운로드
# yum install httpd -y --downloadonly --downloaddir=/opt
# yum update httpd -y --downloadonly --downloaddir=/opt
-> 다운로드 디렉토리 : /opt

----------------------------------------
방법2 :  yum-utils.noarch 패키지 이용
----------------------------------------
1) 설치
# yum -y install yum-utils.noarch

2) rpm 다운로드
# yumdownloader httpd
-> 다운로드 디렉토리 : 현재 디렉토리


----------------------------------------
rpm 설치
----------------------------------------
rpm은 기본적으로 확장자가 .rpm으로 끝나는 패키지를 설치하고 제거하는 기능을 합니다.
물론 이외에도 수없이 많은 기능과 옵션이 존재합니다만 그리 자주 사용되지는 않습니다.

X Winodw용 rpm 패키지 관리자가 있기는 하지만 모두 콘솔용 rpm을 응용하고 있기 때문에
rpm 관리자는 꼭 알아두셔야 합니다.

가장 기본적으로 버전과 도움말을 보겠습니다.

> rpm --version
RPM 버전 - 4.4.2
...

> rpm --help
사용법: rpm [옵션...]
...

빼기(-) 기호가 2개입니다.

다음은 rpm의 기본 옵션입니다.

설치 옵션: -i 또는 -U   (install, upgrade)
제거 옵션: -e             (erase)
질의 옵션: -q             (query)
기타: -v              (정보 표시)
       -h              (진행상태 표시)
       --force        (강제설치)
       --nodeps     (의존성 무시)

자 그럼 패키지를 설치해 보겠습니다.

패키지 install과 upgrade가 있는데 upgrade를 사용하면 먼저 install되어 있는지
확인하므로 upgrade만 사용하셔도 됩니다.

> rpm -Uvh kaffeine-0.8.2-2.i386.rpm

그러면 설치정보를 함께 표시하면서 진행 상황을 "#####..."게 표시해 줍니다.
항상 이렇게 많이 쓰니 외워 두시면 편리합니다.

내 컴퓨터에 설치된 rpm이 ㅤㅁㅕㅈ 개일까요?
질의 옵션을 사용하여 설치된 패키지를 볼 수 있습니다.

> rpm -qa

실로 엄청나게 많습니다. (중간에 멈추려면 Ctrl-C입니다.)
수세는 설치를 전부 rpm으로 하기 때문에 이것을 다 보기란 불가능합니다.

내가 원하는 패키지가 rpm으로 설치되어 있는지 알아보고 싶다면 다음과 같이 표현합니다.

> rpm -qa | grep kaffeine
kaffeine-0.8.2-2

kaffeine이 설치되어 있다면 위와 같이 버전도 함께 출력됩니다.
(파이프라인은 자주 사용되므로 익숙해지셔야 합니다.)

kaffeine이 설치되어 있지 않다면 아무것도 표시하지 않습니다.

이제 kaffeine을 삭제해 보겠습니다. (정말 지우시면 YaST로 다시 깔아야 합니다.)

삭제는 다음과 같이 -e 옵션을 사용합니다. 

> rpm -e kaffeine-0.8.2-2

아무 출력이 없다면 성공적으로 삭제된 것입니다.
GUI처럼 보기 좋지는 않지만 기능은 더 막강합니다.

다음에 프로그램 설치할 때 꼭 이용해 보세요.


귀속년도 : 2010
코드번호 : 749609
 귀속년도 2010
 기준경비율코드 749609
 중분류명 사업지원 서비스업
 세분류명 그외 기타 사업지원 서비스업
 세세분류명 그외 기타 분류안된 사업지원 서비스업
 업태명 N. 사업시설관리 및 사업지원서비스업
 자가율 적용여부 Y
 단순경비율(기본율) 86.4
 기준경비율 20.1
 적용범위및 기준 ?타인의 의뢰에 의하여 당사자 일방이 어느일을 완성할 것을 약정하고 그 일의 결과에 따라 대가를 받는 사업 ?기계조립, 고정, 배치, 시설개체서비스, 대형탱크 조립설치 등 달리 분류되지 않은 도급업


귀속년도 : 2010 코드번호 : 721000
 귀속년도 2010
 기준경비율코드 721000
 중분류명 컴퓨터 프로그래밍, 시스템 통합 및 관리업
 세분류명 컴퓨터시스템 통합 자문, 구축 및 관리업
 세세분류명 컴퓨터시스템 통합 자문 및 구축 서비스업,컴퓨터시설 관리업
 업태명 J. 출판, 영상, 방송통신 및 정보서비스업
 자가율 적용여부 Y
 단순경비율(기본율) 75
 기준경비율 31.6
 적용범위및 기준 ?컴퓨터 유형, 배치, 시스템 및 관련 소프트웨어의 적용 등에 관한 설계?자문 활동을 영위하는 업 ?고객의 컴퓨터시스템을 관리하는 산업활동 (부수적으로 관련 소프트웨어의 설계 및 보완이 관리과정에서 이루어 질 수 있음)


귀속년도 : 2010 코드번호 : 722000
 귀속년도 2010
 기준경비율코드 722000
 중분류명 컴퓨터 프로그래밍, 시스템 통합 및 관리업
 세분류명 시스템·응용 소프트웨어 개발 및 공급업
 세세분류명 응용소프트웨어 개발 및 공급업,시스템? 소프트웨어 개발 및 공급업
 업태명 J. 출판, 영상, 방송통신 및 정보서비스업
 자가율 적용여부 Y
 단순경비율(기본율) 72.5
 기준경비율 26.4
 적용범위및 기준 ?컴퓨터프로그램 번역, 임대, 공급 *컴퓨터 조립생산(→300100) ?범용성 및 주문형 컴퓨터소프트웨어에 관하여 자문, 제작 및 공급을 수행하는 산업활동 ?시스템 및 운영?응용 소프트웨어 제작 ?게임소프트웨어 제작(제작한 게임소프트웨어를 인터넷으로 제공하는 사업체 포함) ?웹디자인(프로그래밍 수반)



1. df -Th

2. file -s /dev/sda1


출처 : userpark.net 


MS SQL Server 백업 / 복구 시나리오  

I. 백업의 종류  

1. 전체 백업(full backup)  
: 데이터 전체를 백업한다. 또한 진행 중인 트랜잭션의 로그도 받는다.  
(로그 전체를 백업 받는 것은 아니다.)  

2. 차등 백업(differential backup)  
: 마지막 전체 백업 이후 변경된 모든 데이터 페이지를 백업한다.  
따라서 전체 백업을 받은 후 차등 백업을 두 번 받았다면 두번째 차등백업은 첫번째  
차등백업의 내용도 포함하고 있다.  
백업 시간은 오래 걸리지만 복원 속도가 빠르다는 장점이 있다.  

3. 트랜잭션 로그 백업(transaction log backup)  
: 일종의 incremental 백업으로, 로그 백업을 받으면 백업 받은 로그는 지워지므로  
동일한 내용이 다시 백업 되지 않는다. 따라서 로그 백업은 전에 받은 로그 백업  
이후의 것만 백업이 된다. 백업은 빠르지만 복원은 전체백업을 복구한 후 각각의  
로그 백업을 복구해야 하므로 시간이 오래 걸린다.  
또한 만약 중간의 로그 백업을 잃어 버리면 그 전의 로그 백업까지의 데이터만 살릴  
수 있다.  


II. 기본적인 복구 시나리오.  
: 다음의 3가지로 나눠볼 수 있다.  
- 전체백업에서 복구  
- differential 백업에서 복구  
- transaction log 백업에서 복구  

1. 전체 백업에서 복원하기  
: 전체 백업에서 복원은 항상 마지막 전체 백업으로부터 복원을 한다.  
다음 작업은 백업을 위한 디바이스를 만들고 데이터를 변경한 후,  
전체백업을 받고 이 백업을 이용해 복원을 하는 과정이다.  

1) 백업 디바이스 만들기  
sp_addumpdevice 'disk','pubs_bak','c:pubsbk.bak'  
(C:에 pubsbk.bak라는 백업 디바이스를 만들고 이름을 pubs_bak라 한다.)  

2) pubs database를 변경한 후 변경을 확인한다.  
create table test1 (id int, name char(10))  
insert test1 values (1, '사용자1')  
select * from test1 (“1, 사용자1”이 나타난다.)  

3) pubs database를 백업 받는다.  
backup database pubs to pubs_bak  

4) 문제를 발생시키고 난 후(pubs를 지운 후), 기존의 백업으로부터 복원을 한다.  
use master  
drop database pubs  
go  
restore database pubs from pubs_bak  

5) 복구된 데이터를 확인한다.  
use pubs  
select * from test1  

2. Differential 백업에서 복구하기  
: differential 백업의 특징은 매번 백업을 받을 때마다 이전 전체백업 이후의 모든  
데이터를 다시 백업 받는다는 것이다. 따라서 모든 데이터가 백업을 받을 때마다  
중복되어 받으므로 백업 시간이 오래 걸린다. 하지만 복원 시에는 전체 백업과  
마지막에 받은 differential 백업만 있으면 되므로 복원속도가 상당히 빠르다.  

다음 작업은 전체 백업을 받은 후 두 번의 differential 백업을 받은 후 복원 시에는  
전체 백업과 두번째의 differential 백업을 가지고 복원하는 과정을 나타낸 것이다.  

또한 특기할 만한 사항은 데이터가 손상되어 database에 접근을 할 수 없는 경우에도  
그때까지의 로그를 받을 수 있다는 것이다.  

backup log … with no_truncate 옵션을 사용하면 된다.  

1) 백업 디바이스를 만든다.  
exec sp_addumpdevice 'disk', 'pubs_full', 'c:pubs_full.bak'  
exec sp_addumpdevice 'disk', 'pubs_diff1', 'c:pubs_diff1.bak'  
exec sp_addumpdevice 'disk', 'pubs_diff2', 'c:pubs_diff2.bak'  
exec sp_addumpdevice 'disk', 'pubs_log', 'c:pubs_log.bak'  
(전체백업용, 각각의 differential 백업용, 로그 백업용)  

2) pubs database를 완전복구 모드로 변경한다.  
alter database pubs set recovery full  
exec sp_helpdb pubs  

3) db를 변경한다.  
use pubs  
go  
create table test_diff (name char(10), score int)  
insert test_diff values('학생1', 100)  
go  
select * from test_diff (=> “학생1, 100”이 출력된다.)  

4) pubs database를 full backup한다.  
backup database pubs to pubs_full  

5) db를 변경한다.  
insert test_diff values('학생2', 900)  
select * from test_diff (=> “학생1, 100”, “학생2, 90”이 출력된다.)  

6) 첫번째 differential 백업을 한다.  
backup database pubs to pubs_diff1 with differential  

7) db를 다시 변경한다.  
insert test_diff values('학생3', 80)  
select * from test_diff  
(=> “학생1, 100”, “학생2, 90”, “학생3, 80”이 출력된다.)  

두번째 differential 백업을 받는다.  
backup database pubs to pubs_diff2 with differential  

9) db를 변경한다.  
insert test_diff values ('학생4', 60)  
select * from test_diff  
(=> “학생1, 100”, “학생2, 90”, “학생3, 80”, “학생4, 60”가 출력된다.)  

10) db에 문제를 발생시킨다.  
SQL Server 서비스를 정지한 후, pubs database의 data file(pubs.mdf)을  
지운다. 다시 SQL Server 서비스를 시작하면, pubs database가 suspect상태가  
된다.  

11) pubs database에 대한 log를 백업 받는다.  
비록 pubs database는 문제가 발생하여 접근할 수 없지만, log는 받을 수 있다.  
backup log pubs to pubs_log with no_truncate  

12) pubs database를 복원한다.  
이때 필요한 백업은 다음과 같다.  

a. 전체 백업.  
b. 두번째 differential 백업.  
c.마지막에 받은 로그 백업.  
use master  
go  
restore database pubs from pubs_full with norecovery  
restore database pubs from pubs_diff2 with norecovery  
restore log pubs from pubs_log  
(이 때 마지막 백업을 복원하는 것 외에는 반드시 with norecovery 옵션을  
붙여줘야 한다. norecovery옵션은 다음에 더 복원될 부분이 남아있다는 뜻.)  

13) select를 이용하여 데이터가 다 복구되었는지 알아본다.  
use pubs  
go  
select * from test_diff (=> differential 백업 이후에 추가된 사항에 대해서도  
완벽하게 복구되는 것을 알 수 있다.)  

3. Transactional Log 백업에서 복구하기  
: transactional log 백업은 전체 백업 후 변경된 부분을 백업을 받는다. 백업 후에는  
로그를 지워버리므로 로그백업을 받으면 이전 백업 받은 다음 부분부터 백업을  
받는다. 이 방법은 백업시간은 단축되지만 복원은 전체 백업과 모든 로그 백업이  
있어야 하므로 시간이 많이 걸린다. 또한 중간의 로그를 잃어 버리면 그 다음 로그는  
백업에 사용될 수 없다.  

이 작업에서는 전체백업을 받은 후 로그 백업을 여러 번 받고 복원 시 전체 백업과  
각각의 로그 백업을 이용하는 것을 보여준다.  

1) 백업에 사용될 디바이스를 만든다.  
(전체백업, 각각의 로그 백업을 위한 디바이스를 만든다.)  

use master  
go  
exec sp_addumpdevice 'disk', 'nwind_full', 'c:nwindfull.bak'  
exec sp_addumpdevice 'disk', 'nwind_log1', 'c:nwindlog1.bak'  
exec sp_addumpdevice 'disk', 'nwind_log2', 'c:nwindlog2.bak'  
exec sp_addumpdevice 'disk', 'nwind_log', 'c:nwindlog.bak'  

2) northwind database를 완전복구 모드로 바꾼다.  
alter database northwind set recovery full  
exec sp_helpdb northwind  

3) database에 새로운 테이블을 만들고 데이터를 입력한다.  
use northwind  
go  
create table nwind_log (id int, name char(10))  
go  
select * from nwind_log  
go  

insert nwind_log values(1, '손님1')  
select * from nwind_log  

4) 전체 백업을 받는다.  
backup database northwind to nwind_full  
go  

5) 데이터를 추가한다.  
insert nwind_log values(2, '손님2')  
select * from nwind_log  

6) 첫번째 로그 백업을 받는다.  
backup log northwind to nwind_log1  

7) 데이터를 추가한다.  
insert nwind_log values(3, '손님3')  
select * from nwind_log  

두번째 로그 백업을 받는다.  
backup log northwind to nwind_log2  

9) 데이터를 추가한다.  
insert nwind_log values (4, '손님4')  
select * from nwind_log  

10) 장애를 발생시킨다.  
(SQL Server 서비스를 멈춘 후 northwnd.mdf 파일을 삭제하고 다시 SQL Server  
서비스를 시작한다. 그러면 northwind database가 suspect 상태로 된다.)  

11) 장애가 발생한 시점까지의 로그를 백업 받는다.  
backup log northwind to nwind_log with no_truncate  

12) 복원을 하는데 이번에는 전체 백업과 모든 로그 백업이 필요하다.  
use master  
go  
restore database northwind from nwind_full with norecovery  
restore log northwind from nwind_log1 with norecovery  
restore log northwind from nwind_log2 with norecovery  
restore log northwind from nwind_log  

13) 데이터를 다시 select하여 모든 데이터가 들어있는 것을 확인한다.  
use northwind  
go  
select * from nwind_log  

III. 고급 복구 시나리오  
- 파일그룹 백업 및 복구  
- 특정 시점으로 복원하기(stopat, stopatmark등)  
- 데이터 파일로부터 복원(sp_attach_db, sp_attach_single_file_db등)  

1. 파일 그룹 백업 및 복구  
: 파일 그룹은 대용량 DB를 유지관리하기 쉽게 하기 위해 생겨난 개념으로 데이터를  
각각 다른 하드에 분산 저장하며 백업과 복원을 각 파일 그룹별로 할 수 있어 전체  
데이터베이스를 백업할 때에 비해 월등한 속도향상과 편의성을 제공한다.  

이 작업에서는 파일 그룹을 생성한 후 각 파일 그룹별로 백업을 받고 그 중 하나의  
데이터 파일이 손상되었을 때 복원하는 방법을 알아 본다.  

1) 파일 그룹 생성.  
CREATE DATABASE fileG ON (NAME = fileG, FILENAME = 'c:datafileG.mdf'  
, SIZE = 10),  
FILEGROUP fileG2 (NAME = fileG2, FILENAME = 'd:datafileG2.ndf'  
, SIZE = 10),  
FILEGROUP fileG3 (NAME = fileG3, FILENAME = 'e:datafileG3.ndf'  
, SIZE = 10)  
LOG ON (NAME = 'fileGLog', FILENAME = 'f:datafileG.ldf', SIZE = 5MB)  
GO  

: fileG database의 data부분을 C:, D:, E:에 나누어서 생성을 하였고,  
로그도 F: 드라이브에 별도로 생성을 하였음.  

2) 데이터를 추가한다.  
use fileG  

create table a(id int)  
create table b(id int)on fileG2  
create table c(id int)on fileG3  
EXEC sp_helpdb fileG  

3) 전체 백업을 받는다.  
backup database fileG to disk='c:full.bak'  

4) primary file Group에 데이터를 집어 넣는다.  
insert a values(1)  

5) primary file Group만 백업을 받는다.  
backup database fileG filegroup='primary'  
to disk='c:file1.bak'  

6) 데이터를 fileG2와 fileG3에도 넣는다.  
insert b values(1)  
insert c values(1)  

7) fileG에 대해 로그 백업을 받는다.  
backup log fileG to disk='c:log1.bak'  

두번째 파일 그룹인 fileG2에 대한 데이터 백업을 받는다.  
backup database fileG filegroup='fileG2' to disk='c:file2.bak'  

9) 데이터를 추가한다.  
insert b values(2)  
insert c values(2)  
insert a values(2)  

10) fileG에 대한 로그 백업을 받는다.  
backup log fileG to disk='c:log2.bak'  

11) 세번째 파일 그룹인 fileG3에 대해 백업을 받는다.  
backup database fileG filegroup='fileG3'  
to disk='c:file3.bak'  

12) 데이터를 업데이트 한다.  
insert c values(3)  
insert a values(3)  
insert b values(3)  

13) 이 때 장애가 세번째 파일 그룹의 데이터 파일이 손상되었다.  
(fileG3.ndf를 삭제한다.)  

14) 이 때까지의 로그를 받는다.  
backup log fileG to disk='c:log3.bak'  
with init,no_truncate  

15) 이제 손상된 fileG를 복원한다.  
이때는 전체 백업을 복구하는 것이 아니라 세번째 파일 그룹에 대한 백업과  
마지막에 받은 로그를 가지고 복원을 한다.  

restore database fileG filegroup='fileG3'  
from disk='c:file3.bak'with norecovery  

restore log fileG from disk='c:log3.bak'  

16) fileG의 모든 table을 select 해 본다.  
use fileG  
go  
select * from a  
select * from b  
select * from c  

모든 항목들이 다 나와 있음을 알 수 있다.  
File Group의 특징은 대용량 데이터 베이스를 백업 및 복원을 할 때 모든  
데이터에 대하여 하는 것이 아니라 file Group 별로 나누어 백업을 하고 특정  
파일그룹이 깨진 경우에는 해당되는 file Group의 백업만 복원해 주면 되므로  
백업과 복원에 시간이 적게 걸리게 되어 유지보수에 유용한 모델이다.  

2. 특정 시점으로 복원하기.  
: stopat, stopatmark, stopbeforemark  

1) stopat  
: 데이터를 원하는 시점으로 되돌릴 수 있는 방법.  
다음과 같은 경우에 적용된다.  
a. 오전 9:00 전체백업  
b. 다양한 작업을 함.  
c. 오전 10:00 실수로 where 절 없이 delete문 수행하여 테이블 A의 모든 데이터를  
지움.  
d. 오전 10:30분 로그 백업.  
e. 11:00 대량으로 잘못된 작업이 수행되었음을 발견.  

이 때는 다음과 같이 작업해 준다.  
ㄱ. 전체백업을 복원한다.  
ㄴ. 백업된 로그를 복원하면서 다음과 같은 옵션을 준다.  
RESTORE LOG … FROM …  
WITH STOPAT = ‘2001-12-24 10:00:01  

예제] pubs db를 이용하여 stopat을 테스트 해 본다.  
a. 현재의 titles.price 컬럼의 가격이 얼마로 시작되는지 확인한다.  
SELECT TOP 1 price FROM pubs..titles (19.99가 나온다.)  
b. pubs database를 전체복구 모드로 바꾼다.  
ALTER DATABASE pubs SET RECOVERY full  
Exec sp_helpdb pubs  
c. pubs database를 전체백업 받는다.  
BACKUP DATABASE pubs to disk = ‘c:pubsfull.bak’  
d. titles의 값을 두배로 만든다.  
UPDATE pubs..titles SET price=price*2  
SELECT TOP 1 price FROM pubs..titles (39.98이 나온다.)  
e. log 백업을 받는다.  
BACKUP LOG pubs to disk = ‘c:pubslog1.bak’  
f. 한번 더 가격을 인상한다.  
UPDATE pubs..titles SET price=price*2  
SELECT TOP 1 price FROM pubs..titles (79.96이 나온다.)  
g. 서버의 시간을 기록한다.  
SELECT getdate()  
h. 이번에는 titles의 값을 전부 100으로 변경한다.  
(이 작업은 잠시 시차를 두고 하는 것이 좋다.)  
UPDATE pubs..titles SET price = 100  
SELECT TOP 1 price FROM pubs..titles (100이 나온다.)  
i. 다시 한번 로그 백업을 받는다.  
BACKUP LOG pubs to disk = ‘c:pubslog2.bak’  
j. 지금 현재 full 백업과 로그 백업이 2개가 있다.  
각각을 사용하여 복원을 시도해 보면 다음과 같은 결과가 나온다.  

Full 백업만 사용하여 복원한 경우  
RESTORE DATABASE pubs FROM disk = ‘c:pubsfull.bak’  
SELECT TOP 1 price FROM pubs..titles (19.99가 나온다.)  

첫번째 로그까지 사용하여 복원한 경우  
RESTORE DATABASE pubs FROM disk = ‘c:pubsfull.bak  
WITH NORECOVERY  
RESTORE LOG pubs FROM disk = ‘c:pubslog1.bak’  
SELECT TOP 1 price FROM pubs..titles (39.98이 나온다.)  

두번째 로그까지 사용하여 복원한 경우  
RESTORE DATABASE pubs FROM disk = ‘c:pubsfull.bak’  
WITH NORECOVERY  
RESTORE LOG pubs FROM disk = ‘c:pubslog1.bak’  
WITH NORECOVERY  
RESTORE LOG pubs FROM disk = ‘c:pubslog2.bak’  
SELECT TOP 1 price FROM pubs..titles (100이 나온다.)  
잘못된 작업을 하기 전의 데이터로 돌리려면 STOPAT 옵션을 사용한다.  

RESTORE DATABASE pubs FROM disk =’c:pubsfull.bak’  
WITH NO RECOVERY  
RESTORE LOG pubs FROM disk = ‘c:pubslog1.bak’  
WITH NORECOVERY  
RESTORE LOG pubs FROM disk = ‘c:pubslog2.bak’  
WITH STOPAT = ‘2001-12-24 14:53:07.310’(잘못된 작업을 하기 전의 시간)  

SELECT TOP 1 price FROM pubs..titles  
(79.96이 나온다. 즉, 잘못된 작업을 하기 전의 값이 나온다.)  

일반적으로 STOPAT 옵션은 시간을 기억해서 올바른 시간을 입력해야 한다는 부담이  
있다. 이것을 극복하기 위해 나온 옵션이 STOPATMARK 옵션이다.  

2) STOPATMARK, STOPBEFOREMARK  
: STOPAT옵션이 시간을 기반으로 되돌리는 작업을 했다면, STOPATMARK는 해당 MARK까지  
복원한다. 이를 사용하기 위해서는 트랜잭션을 시작할 때 마크를 지정해야 한다.  

BEGIN TRAN tran1 WITH MARK  
… (해당작업)  
COMMIT  

복원 시는 다음과 같이 사용한다.  

RESTORE LOG … FROM …  
WITH STOPATMARK = ‘tran1’  

Transaction이 여러 번 사용되었으면 다음과 같이 AFTER 옵션을 준다.  
WITH STOPAT = ‘tran1’ AFTER 2001-12-24 14:53:07.310  
STOPATMARK는 해당 트랜잭션까지 복원하고,  
STOPBEFOREMARK는 해당 트랜잭션 직전에서 복원을 중지한다.  

예제] stopatmark를 이용하여 데이터 복구하기  
a. pubs database를 Full 복원모드로 변경한 후 전체 백업을 받는다.  
ALTER DATABASE pubs SET RECOVERY FULL  
BACKUP DATABASE pubs to disk = ‘c:fullpubs.bak’  
b. sales table에 총 21건의 데이터가 있음을 확인하다.  
use pubs  
select count(*) from sales  
c. 다음과 같은 트랜잭션에 표시를 주고 시작한다.  
Begin tran tran1 WITH MARK  
set rowcount 10  
delete sales  
set rowcount 0  
select count(*) from sales  
COMMIT (10개의 행이 지워져 총 11행이 남는다.)  
d. 다시 모든 행을 지운다.  
delete sales  
e. 로그 백업을 한다.  
BACKUP LOG pubs to disk = ‘c:log1.bak’  
f. 백업을 모두 복원한다.  
use master  
RESTORE DATABASE pubs FROM disk = ‘c:fullpubs.bak’  
WITH NORECOVERY  
RESTORE LOG pubs FROM disk = ‘c:log1.bak’  
SELECT count(*) from pubs..sales  
( 0개가 나온다.)  
g. STOPATMARK를 사용하여 transaction의 끝까지만 복원을 한다.  
RESTORE DATABASE pubs FROM disk = ‘c:fullpubs.bak’  
WITH NORECOVERY  
RESTORE LOG pubs FROM disk = ‘c:log1.bak’  
WITH STOPATMARK = ‘tran1’  
SELECT count(*) from pubs..sales  
(11건의 데어터가 나온다.)  

h. STOPBEFOREMARK를 사용하여 표시된 트랜잭션 작업 직전까지만 복원한다.  
RESTORE DATABASE pubs FROM disk = ‘c:fullpubs.bak’  
WITH NORECOVERY  
RESTORE LOG pubs FROM disk = ‘c:log1.bak’  
WITH STOPBEFOREMARK = ‘tran1’  

SELECT count(*) from pubs..sales  
(21건이 나온다.)  

3) 데이터 파일로부터 복원  
: sp_attach_db, sp_detach_db, sp_attach_single_file_db  
DB를 복구하려고 하는데 백업본이 없고 데이터 파일과 로그 파일만 있는 경우 또는  
데이터 파일만 있는 경우 이 파일들을 이용하여 데이터베이스를 복원할 수 있다.  

a. sp_attach_db  
: 데이터와 로그 파일을 모두 가지고 있는 경우 사용되는 방법으로 사용법은  
다음과 같다.  

sp_attach_db ‘test’, ‘c:datatest.mdf’,  
‘c:datatest_log.ldf’  
(test.mdf와 test_log.ldf 두 개의 파일이 있을 때 이 파일을 이용하여  
test라는 database를 만든다.)  

b. sp_attach_single_file_db  
: 로그 파일은 없고 데이터 파일만 남은 경우 사용한다.  

sp_attach_single_file_db ‘test’, ‘c:datatest.mdf’  
(test.mdf 데이터 파일만 가지고 test db를 다시 만들었다.  
이 때 로그는 새로 만들어 진다.)  

c. sp_detach_db  
: 기존의 database를 data와 log 파일만 남겨 놓은 채 지워버린다.  

sp_detach_db ‘test’  
(test database를 지운 후 데이터 파일과 로그 파일만 남긴다.  
이 프로시저는 sp_attach_db와 같이 사용되어 한 서버에  
있는 데이터베이스를 다른 서버로 이동할 때 사용된다.)  

예제] sp_detach_db와 sp_attach_db, sp_attach_single_file_db 를 이용하기  

a. pubs database를 detach 한다.  
sp_detach_db pubs  

b. select를 수행하여 pubs database의 테이블을 query한다.  
SELECT * from pubs..titles  
(pubs database를 찾을 수 없다고 나온다.)  

c. pubs.mdf와 pubs_log.ldf를 c:data 폴더로 옮긴다.  

d. pubs database를 attach한다.  
sp_attach_db 'pubs', 'c:datapubs.mdf',  
'c:datapubs_log.ldf'  

e. SELECT를 하여 pubs database가 다시 사용가능한 것을 확인한다.  
SELECT * from pubs..titles  
(결과값이 출력되는 것을 확인할 수 있다.)  

f. 다시 pubs database를 detach한다.  
sp_detach_db pubs  

g. 로그파일(pubs_log.ldf)을 삭제한다.  

h. sp_attach_single_file_db를 실행한다.  
sp_attach_single_file_db ‘pubs’, ‘c:datapubs.mdf’  
(pubs database가 다시 생기고 log도 다시 생성되어 있음을 알 수 있다.)
 

'MSSQL' 카테고리의 다른 글

(펌) T_SQL 같은 그룹의 데이터를 붙여서 출력하기  (0) 2011.12.27
T-SQL) group by 결과를 가로로 출력  (0) 2011.12.27
(펌) MSSQL 2005 백업계획작성  (0) 2011.10.20
(펌) MSSQL lock & blocking  (0) 2010.12.07
(펌) MSSQL Lock  (0) 2010.12.07

+ Recent posts