Database/MSSQL work

커서 cursor

웨이칭 2020. 11. 13. 11:24

행을 가르키는것? 행의 집합정도

 

-- 선언, 오픈, 패치, (사용 후 ~ ) 클로즈/릴리즈(해제) 순서

 

 

 

커서의 내부 작동 방식

1. local, global

 

 

 

2. 원본 테이블을 DB에 복사해 놓을때, 어떻게 복사할지에대한 방법들 : 

static, keyset, dynamic, fast_foward : 

--static

커서에서 사용할 데이터를 모두 DB에 복사 한 후 사용하므로 원본테이블 값이 바뀌어도 원래값이 나온다.

 

--keyset

1. 테이블에서 key값만 복사하여 메모리상에서 매번 원래 DB를 조회함. 따라서 update된 내용은 다 보이고 insert된 내용은 안보임
2. 유니크 제한조건이 걸려있지 않으면 보이지 않음(스테틱으로 자동변환됨)

 

-- 동적(dynamic)

패치 될때마다 매번 키를 하나씩 가져와서 확인 하므로, 커서 중간에 업데이트 할때에도 업데이트 된 내용 확인 가능함, 하지만 느림

 

--fast_forward

forward only 이면서 read only 인것 (전진만 함) : 순차적으로 읽기만 하므로 select할거면 제일 빠름 

 

 

 

*서버옵션을 변경하지 않으면 디폴트로 DYNAMIC이 생성됨

*DYNAMIC이 성능에 제일 나쁜 옵션

 

 

 

 

기본

-- 선언

declare cursor_salesTbl cursor global
for select sales from salesTbl;

 

 

-- 오픈

open cursor_salesTbl

declare @sales int
declare @cnt int = 0
declare @totalSales int = 0

 

 

-- 패치

fetch next from cursor_salesTbl into @sales

 

 

-- 선언

while @@FETCH_STATUS = 0
begin 
	set @cnt += 1
	set @totalSales += @sales
	fetch next from cursor_salesTbl into @sales
end
print '고객 방문 평균 구매 금액 ==> ' + cast(@totalSales/@cnt as char(10))

 

 

--close

close cursor_salesTbl

--릴리즈

deallocate cursor_salesTbl

 

 

 

 

 

 

 


실습

 

사용환경만들기

use tempdb

create database cursorDB
go

use cursorDB
go

select * into cursorTbl
from AdventureWorks2019.Sales.SalesOrderDetail
go

select * from cursorTbl

 

 

 

-- LIneTotal의 합과 평균 구하기

select sum(LineTotal), avg(LineTotal) from cursorTbl

 

-- 위의 내용을 커서를 이용해 구하기

declare cursor_cursorTbl cursor global fast_forward
for select LineTotal from cursorTbl

open cursor_cursorTbl

declare @LineTotal money
declare @cnt int
declare @sumLineTotal money

set @sumLineTotal = 0
set @cnt = 0

fetch next from cursor_cursorTbl into @LineTotal

while @@FETCH_STATUS = 0 -- LINETOTAL 값이 다 돌면 -1로 바뀜
begin 
	set @cnt += 1
	set @sumLineTotal += @LineTotal
	fetch next from cursor_cursorTbl into @LineTotal
end

print '총 합계 ==> ' + cast(@sumLineTotal as char(20))
print '평균 ==> ' + cast(@sumLineTotal/@cnt as char(20))

close cursor_cursorTbl;
deallocate cursor_cursorTbl;

go

 

 

 

 

 

 

 

 

 

-- global 커서 생성

declare cursor_cursorTbl cursor
for select LineToTal from cursorTbl

 

-- 쿼리문으로 조회하기

declare @result cursor
exec sp_describe_cursor @cursor_return = @result OUTPUT,
	@cursor_source = N'GLOBAL',
@cursor_identity = N'cursor_cursorTbl'

fetch next from @result
while @@FETCH_STATUS <> -1
fetch next from @result


deallocate cursor_cursorTbl

 

 

 

 

 

 

-- local cursor 생성

declare cursor_cursorTbl cursor LOCAL
for select LineToTal from cursorTbl;

declare @result cursor
exec sp_describe_cursor @cursor_return = @result OUTPUT,
	@cursor_source = N'LOCAL',
	@cursor_identity = N'cursor_cursorTbl'

fetch next from @result
while @@FETCH_STATUS <> -1
fetch next from @result

go

글로벌과 동일?

--
declare cursor_cursorTbl cursor 
for select LineToTal from cursorTbl;


declare @result cursor
exec sp_describe_cursor @cursor_return = @result OUTPUT,
	@cursor_source = N'GLOBAL',
	@cursor_identity = N'cursor_cursorTbl'

fetch next from @result
while @@FETCH_STATUS <> -1
fetch next from @result

deallocate cursor_cursorTbl

go

 

 

 

 

 


-- static (빠르게 읽기만 할거다)

declare cursor_cursorTbl cursor global static
for select LineToTal from cursorTbl

open cursor_cursorTbl

fetch next from cursor_cursorTbl

-- static은 readolny라서 업데이트 해도 변동없음
update cursorTbl set SalesOrderID = 0;

close cursor_cursorTbl
deallocate cursor_cursorTbl

go

 

 

 

 

 


 

 

 

 

-- fecth next/last/prior/first 사용하기

use OnlineShopDB
go

select custName, addr from customersTbl
declare curor_customersTbl cursor scroll
for select custName, addr from customersTbl

open curor_customersTbl

--fetch next
declare @name nvarchar(10)
declare @addr nvarchar(5)
fetch next from curor_customersTbl into @name, @addr
select @name, @addr

--fetch last
/*
declare @name nvarchar(10)
declare @addr nvarchar(5)
fetch last from curor_customersTbl into @name, @addr
select @name, @addr
*/

-- fetch prior 이전
/*
declare @name nvarchar(10)
declare @addr nvarchar(5)
fetch prior from curor_customersTbl into @name, @addr
select @name, @addr
*/

-- fetch first
/*
declare @name nvarchar(10)
declare @addr nvarchar(5)
fetch first from curor_customersTbl into @name, @addr
select @name, @addr
*/


close cursor_cursorTbl
deallocate cursor_cursorTbl

go

 

 

 

 

 keyset 으로 cursor만들기 (key값 들고와 메모리에 올림)

 

-- keyset test1.  

create table keysetTbl(id int, txt char(5));
go

insert into keysetTbl values(1, 'AAA');
insert into keysetTbl values(2, 'BBB');
insert into keysetTbl values(3, 'CCC');

-- cursor 생성
declare cursor_keysetTbl cursor global forward_only keyset
for select id, txt from keysetTbl

-- 키셋이 유니크제약조건으로 지정되지 않아서 자동으로 스테틱으로 변환되어 출력됨
declare @result cursor
exec sp_describe_cursor @cursor_return = @result OUTPUT,
	@cursor_source = N'GLOBAL',
	@cursor_identity = N'cursor_keysetTbl'

fetch next from @result
while @@FETCH_STATUS <> -1
fetch next from @result


deallocate cursor_keysetTbl

 

 

 

 

 

-- keyset test2. 

for select id, txt from keysetTbl	--"생성된 커서는 요청한 유형이 아닙니다." 라고 나옴