/* 테이블생성하고 데이터넣기*/

set statistics io off

if OBJECT_ID('tbxx') is not null drop table tbxx

go

if OBJECT_ID('tbyy') is not null drop table tbyy

go

create table tbxx (aint int primary key, atext varchar(10), atext1 varchar(11), acheck tinyint);

go

create table tbyy (aint int primary key, atext varchar(10), atext1 varchar(11), acheck tinyint);

go

set nocount on

declare @a int

set @a = 1;

while @a <= 100000

begin

    insert into tbxx values(@a, '','',0);

    insert into tbyy values(@a, '','',0);

       set @a += 1;

end

 

/* 돌려보기*/

set statistics io on

set nocount on

 

declare @a int

set @a = 1000091;

 

-- Merge

merge into dbo.tbxx as target

using(values(@a))

as source (aint)

on target.aint = source.aint

when matched then

       update set atext = 'a1', atext1 = 'b1', acheck = 1

when not matched by target then

       insert (aint, atext, atext1, acheck) values(aint, '','',0);

-- 옛날방식(뭐 여러가지 방법이 있겠지만..)

update dbo.tbyy set atext ='a',atext1 ='b',acheck =1 where aint =@a;

if @@ROWCOUNT=0

begin

       insert into dbo.tbyy values(@a,'','',0);

end

실행 결과.
데이터가 존재하지 않는 경우

데이터가 존재하는 경우

데이터가 없는 경우는 확실히 Merge 를 사용하는 것이 유리 하다.

데이터가 있는 경우에도 io 나 계획이 똑같은 것을 보니 내부적으로 아름답게 돌려 주나 보다..

그러나 Merge가 아무리 아름답게 돌아간대도 데이터가 있는지 없는지는 판단 해야 할거 아닌가..

좀더 확실하게 테스트 해보기 위해 위와 똑같은 상황에서 인덱스만 제거 하고(풀스캔) 다시 실행..

데이터가 존재하지 않는 경우

데이터가 존재하는 경우

오 두번다 예전에 사용하던 방식이 더 좋게 나왔다. 

풀스캔 하고 데이터가 있는 경우에도 기존 방식이 더 좋게 나왔다.
--Merge
Table 'tbxx'. Scan count 1, logical reads 365, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--기존 쿼리
Table 'tbyy'. Scan count 1, logical reads 364, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tbyy'. Scan count 0, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

특히 풀스캔을 하고 데이터가 있는 경우에는 io 도 기존 방식이 더 좋게 나왔다.
--Merge
Table 'tbxx'. Scan count 1, logical reads 365, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--기존 쿼리
Table 'tbyy'. Scan count 1, logical reads 364, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Merge 는 match 와 not match 두번 모두 체크 할테고..

기존 쿼리는 update 하고 @@ROWCOUNT 가 0 이면 상대적으로 비용이 적은 INSERT 한번 하고 끝날테니..

리소스를 더 소비하는건 맞는 말이 되겠지.


그리고 using 절 에 위에서 테스트한 변수 말고 select 절도 올수 있다.


하지만 with(readuncommitted) 같은 잠금에 대한 힌트를 사용한다면 데이터의 무결성이 깨질 수도 있다.

/* 테이블 한개 더 만들고 */

ifOBJECT_ID('tbz')isnotnulldroptabletbz

go

createtabletbz (aint int,acheck int);

go

declare@a int

set@a =1;

while@a <=10

begin

       insertintotbz values(@a,0);

       set@a +=1;

end

go

/*전체데이터의 반을 tbx 와 다르게 변경*/

updatetbz setaint *=10 whereaint >5

/* 첫번째 세션에서 실행 */
begin tran
update tbz set acheck = 1 where aint = 1

/* 두번째 세션에서 실행 */
begin tran

merge into dbo.tbz as target

using

(

    select

          a.aint aintz

        , b.aint aintx

        , isnull(a.acheck,0) + isnull(b.acheck,0)

    from dbo.tbz a with(readuncommitted)

    left outer join

        dbo. tbx b on

            a.aint = b.aint

)

as source (aintz, aintx, sumdata)

on target.aint = source.aintz

when matched then

       update set acheck += sumdata

when not matched by target then

       insert (aint, acheck) values(aintz, 0);

 여기서 첫번째 세션에서 실행한 구문을 ROLLBACK 시키고

두번째 세션의 구문을 COMMIT 해버리면

tbz 의 값이 ROLLBACK 되기전 값으로 계산된 값이 UPDATE 되어진다.

using 절에 select 를 사용 할때는 이점의 유의 해야지 싶다.

또한 현재 database 의 TRANSACTION ISOLATION LEVEL 에 따라서 도

의도하지 않은 값이 나 올 수 있으니 요것도 주의 해야지~

뭐든 잘쓰면 약이 되고 못쓰면 독이 되는 법이 것지만..

이 게시물을..