Hyun Soo, Lim

SSMS 사용하는데 유용한 툴이 있어 공유드립니다.

아래의 사이트에 자세하게 기능에 대해서 자세히 설명되어 있습니다. 그 중에서 제가 맘에 들었던 기능 간단히 소개드립니다. SQL Server 2011 CTP1에서도 사용 가능합니다.

1. 쿼리 기본 문구를 약자로 생성하는 기능
2. 실행한 쿼리 기록 보기
3. 실행계획 이미지로 캡쳐해주기
4. 가장 맘에 드는 것은 각 서버별로 접속할 때 서버별로 색상을 지정할 수 있다는 겁니다.
    색을 넣어두면 개발서버인 줄 알고 실수하는 경우를 줄일 수 있지 않을까합니다.

    아래는 같은 서버에 인스턴스명으로 접속(좌)한 경우와 (local)(우)로 접속했을 때의 차이를 보여줍니다.
   

출처 : http://www.ssmstoolspack.com/

오라클에만 존재하던 시퀀스 오브젝트가 드디어 SQL Server 2012 버전에 추가되었습니다.
오라클로 DB를 입문했던 저로써는 매우 반가운 소식이였습니다. 그래서 가볍게 테스트 해 본 결과 공유드립니다.

1. 기본 문법

CREATE SEQUENCE [schema_name . ] sequence_name

        [ <sequence_property_assignment> [ ,…®|n ] ]

    [ ; ]

 

<sequence_property_assignment>::=

{

    [ AS { built_in_integer_type | user-defined_integer_type } ]

    | START WITH <constant>

        | INCREMENT BY <constant>

        | { MINVALUE <constant> | NO MINVALUE }

        | { MAXVALUE <constant> | NO MAXVALUE }

        | { CYCLE | NO CYCLE }

        | { CACHE [<constant> ] | NO CACHE }

}

 

NEXT VALUE FOR [ database_name . ] [ schema_name . ]  sequence_name

   [ OVER (<over_order_by_clause>) ]

 

2. 시퀀스 객체를 활용한 채번 방식/옵션 변경에 따른 채번 성능 테스트 (참고. 개인 PC에서 테스트 진행함)
2-1. NOCACHE 에서 채번 테스트

CREATE SEQUENCE SeqNo_nocache

START WITH 1

INCREMENT BY 1

no Cache;

GO

 

declare

@seqval int = 0

,       @Cnt    int = 0;

 

select getdate();

 

while (@Cnt <= 1000000)

begin

SELECT @seqval = NEXT VALUE FOR SeqNo_nocache;

set @Cnt = @Cnt + 1;

end

 

select GETDATE();

 

Result> 21

2010-12-17 20:28:32.870

2010-12-17 20:28:53.127

2-2. CACHE 10/50/100 상태에서 채번 테스트

CREATE SEQUENCE SeqNo_Cache10

START WITH 1

INCREMENT BY 1

Cache 10;

GO

 

SELECT * FROM sys.sequences WHERE name = 'SeqNo_Cache10'

 

declare

           @seqval int = 0

,          @Cnt    int = 0;

 

select getdate();

 

while (@Cnt <= 1000000)

begin

           SELECT @seqval = NEXT VALUE FOR SeqNo_Cache100;

           set @Cnt = @Cnt + 1;

end

 

select GETDATE();

 

Result> Cache 10 : 약9초

2010-12-17 20:31:40.393

2010-12-17 20:31:49.953

 

Result> Cache 50 : 약9초

2010-12-17 20:32:41.960

2010-12-17 20:32:50.643

 

Result> cache 100 : 약9초

2010-12-17 20:34:24.297

2010-12-17 20:34:33.090

2-3. sp_sequence_get_range 사용하여 채번 (하나씩)

declare

           @seqval int = 0

,          @Cnt    int = 0

,          @range_first_value sql_variant

,          @range_first_value_output sql_variant;

 

select getdate();

 

while (@Cnt <= 1000000)

begin

           EXEC sp_sequence_get_range

                @sequence_name = N'seqno_cache10'

           ,    @range_size = 1

           ,    @range_first_value = @range_first_value_output OUTPUT ;

 

           set @Cnt = @Cnt + 1;

end

 

select GETDATE();

 

 

Result> 54

2010-12-17 20:38:00.330

2010-12-17 20:38:54.593

2-4. sp_sequence_get_range 사용하여 채번 (필요한 범위를 한방에~ )

declare

           @seqval int = 0

,          @Cnt    int = 0

,          @range_first_value sql_variant

,          @range_first_value_output sql_variant

,          @range_last_value_output sql_variant;

 

select getdate();

 

EXEC sp_sequence_get_range

           @sequence_name = N'seqno_cache10'

,          @range_size = 1000000

,          @range_first_value = @range_first_value_output OUTPUT

,          @range_last_value = @range_last_value_output output;

 

select @range_first_value_output, @range_last_value_output

 

select GETDATE();

 

Result> 0

2010-12-17 20:41:02.427

2010-12-17 20:41:02.427

3. 하나의 쿼리에서 동일한 시퀀스를 여러번 호출하면 어떻게 될까?
select next value for SeqNo_Cache10, next value for SeqNo_Cache10

 

Result> 동일한 값이 나온다.
5000151 5000151


4. 시퀀스에 over 구문은 왜 있을까?
4-1. over 절 사용 안하는 경우

select
        x.name
,       next value for SeqNo_Cache10  as seqNo
from (select top 100 name from sys.columns) x

Result
> 이름이 정렬되어지지 않은 상태로 채번되어진다.


4-2. over절 사용하는 경우 (update, merge 구문과 같이 사용 불가함)

select 
        x.name
,       next value for SeqNo_Cache10 over (order by name) as seqNo

from (select top 100 name from sys.columns) x

Result> 이름 정렬순으로 낮은 순서로 채번된다.


5. 시퀀스 번호 재시작하기

ALTER SEQUENCE dbo.Seq1 RESTART WITH 1;

6. 테스트하고 느낀 점
 > 캐싱 개수에 상관없이 절대량 처리 속도는 거의 비슷하다. 동시성만 고려하여 캐싱 수치를 적절히 잡으면 될 것 같다.
 > 캐싱을 사용할 경우 DB서버를 재시작하면 캐싱된 번호가 사라져버릴꺼라고 생각했는데, 유지되어진다. (좋다~ ^^)
 > 대량 범위를 확보하고 싶은 경우 프로시져를 반드시 쓰기를 추천합니다.
 > Identity와 Sequence를 동시에 사용할 수 있다니, 기대만빵입니다. 어느 것을 사용할지는 사용자의 선택~

7. Itzik Ben-Gan Blog에 Sequence 에 대한 퀴즈도 있으니 관심 있으신 분은 참고하세요~

*. 참고 자료
http://msdn.microsoft.com/en-us/library/ff878058(v=SQL.110).aspx
http://www.sqlmag.com/blogs/puzzled-by-t-sql/tabid/1023/entryid/75978/TSQL-Challenge-with-Denali-s-Sequence-Object.aspx
http://sqlserverpedia.com/blog/sql-server-bloggers/2-of-my-favorite-new-t-sql-features-in-sql-server-denali-sql-11/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+sqlserverpedia+%28SQLServerPedia%29&utm_content=Google+Reader

 


이 글을 작성한 목적은 일정 기간동안 컬럼의 값을 증가시키다가 일정기간마다 초기화가 필요한 경우에 활용하면 좋을 듯 하여
블로그 일간 방문자수 보여주기 기능을 사례로 들어서 테스트해 본 것입니다.

원활한 테스트를 위해서 “일간 방문자수 보여주기” 기능에 대한 정의는 아래와 같이 단순화하겠습니다.
    1. 임의의 사용자가 블로그에 방문하면 일간 방문자수를 +1 증감시켜준다.
    2. 날짜가 변경되면 일간 방문자수를 0으로 초기화되며, 새로 방문자가 올 경우 1로 보여준다.
    3. 이전날짜의 방문자수를 따로 보여주지 않는다.

제가 생각해 본 이 방식의 장점은 아래와 같습니다.
    1. 매일 0시에 대량의 업데이트를 할 필요가 없다.
    2. 접근이 없는 블로그에 대해서는 불필요한 업데이트를 하지 않아도 된다.
   3. 웹서비스의 경우 불특정 다수를 상대로 서비스하기 때문에, 사용자가 많아짐에 따라서 순간적인 병목이 발생할 수 있는데요.
      필요한 경우에만 데이터를 업데이트 함으로써 부하를 분산할 수 있습니다.

그럼 이제 테스트를 진행하도록 하겠습니다.

use
tempdb

go

 

-- @@ 예제 테이블 생성 및 데이터 추가

drop table BlogMainTable

create table BlogMainTable (

       blogId          varchar(50)         -- 블로그이름

,      VisitorCount    int                 -- 오늘 블로그에 방문한 방문자수

,      lastModified    datetime            -- 블로그 최종데이터 변경일자

)

 

insert into BlogMainTable values ( 'Blog_01', 5, getdate() - 1 )  -- 어제 50명이 방문하고 오늘 방문자가 없는 블로그

insert into BlogMainTable values ( 'Blog_02', 10, getdate())      -- 오늘 10명이 방문한 블로그

 

 

-- @@ 어제 방문자가 5명이였고, 오늘 방문자가 없는 블로그 조회 쿼리

select

       blogId

,      VisitorCount as VisitorCount_real

,      case

             when DATEADD( day, DATEDIFF(day, 0, lastModified), 0) < DATEADD( day, DATEDIFF(day, 0, GETDATE()), 0) then 1                      -- 최종변경일시가 어제라면 1을 보여줌

             when DATEADD( day, DATEDIFF(day, 0, lastModified), 0) = DATEADD( day, DATEDIFF(day, 0, GETDATE()), 0) then VisitorCount           -- 최종변경일시가 오늘이면 +1된 건수를 보여줌

             else 0

       end as VisitorCount_view

,      lastModified

from BlogMainTable

where blogId = 'Blog_01'







-- @@  오늘 10명이 방문한 블로그

select

       blogId

,      VisitorCount as VisitorCount_real

,      case

             when DATEADD( day, DATEDIFF(day, 0, lastModified), 0) < DATEADD( day, DATEDIFF(day, 0, GETDATE()), 0) then 1                      -- 최종변경일시가 어제라면 1을 보여줌

             when DATEADD( day, DATEDIFF(day, 0, lastModified), 0) = DATEADD( day, DATEDIFF(day, 0, GETDATE()), 0) then VisitorCount           -- 최종변경일시가 오늘이면 +1된 건수를 보여줌

             else 0

       end as VisitorCount_view

,      lastModified

from BlogMainTable

where blogId = 'Blog_02'







-- @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-- @@ 실제 적용시에는 업데이트와 조회를 한방에 처리하면 좀 더 비용을 절감할 수 있습니다.
-- @@ 업데이트와 조회를 한번에 처리하는 예제는 아래와 같습니다.


declare

       @blogId                   varchar(50)

,      @VisitorCount_before       int = 0

,      @VisitorCount_view         int = 0

,      @lastModified              datetime

 

-- 현재 테이블에 저장된 데이터 확인

select * from BlogMainTable where blogId = 'Blog_01'




 



--
데이터 변경하기

update bm

set

       @blogId = blogId

,      @VisitorCount_view = VisitorCount = case

                                              when DATEADD( day, DATEDIFF(day, 0, lastModified), 0) < DATEADD( day, DATEDIFF(day, 0, GETDATE()), 0) then 1                      -- 지난달의 기록이면 노출

                                              when DATEADD( day, DATEDIFF(day, 0, lastModified), 0) = DATEADD( day, DATEDIFF(day, 0, GETDATE()), 0) then VisitorCount + 1       -- 이번달의 Sum이면 합계금액 노출

                                              else 0

                                           end

,      @lastModified = lastModified = getdate()

from BlogMainTable as bm

where blogId = 'Blog_01'

 

-- 업데이트된 결과 확인(Blog_01의 경우 어제일자이므로 1로 변경됨)

select @blogId as blogId , @VisitorCount_view as VisitorCount_view, @lastModified as lastModified