안녕하세요. SQLER의 코난 김대우입니다.
이번 강좌에서는, 9-2. 커서 - 커서 구문을 진행 하겠습니다.
SQLER에서 진행되는, 챗GPT와 함께 배우는 SQL Server 강좌 목록
이번에 진행할 강좌는 커서 구문입니다.
TL;DR
커서 구문은 DECLARE, OPEN, FETCH 반복, CLOSE, DEALLOCATE 순서로 진행됩니다. 여러 종류의 커서를 SQL 코드로 설명하고, 각 작업별 주의사항과 수행 방식을 진행합니다.
커서 구문 리뷰
이전 강좌에서 우편물과 우편번호 테이블을 이용해 잘 못 기재된 우편번호를 수정하는 작업을 커서를 실행해 해결했습니다. 커서 구문을 좀 더 살펴보도록 하겠습니다.
커서 선언
DECLARE 구문을 사용해 커서를 선언합니다.
--커서 선언 DECLARE curMailTask CURSOR FAST_FORWARD FOR SELECT mail_idx, mail_num, mail_address FROM Mails;
DECLARE CURSOR 구문 정보
DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR FOR select_statement [ FOR { READ ONLY | UPDATE [ OF column_name [ ,...n ] ] } ] [;] Transact-SQL Extended Syntax DECLARE cursor_name CURSOR [ LOCAL | GLOBAL ] [ FORWARD_ONLY | SCROLL ] [ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ] [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ] [ TYPE_WARNING ] FOR select_statement [ FOR UPDATE [ OF column_name [ ,...n ] ] ] [;] |
커서의 종류는 FORWARD_ONLY, READ_ONLY 등의 여러 방식과 조합이 있습니다. 제가 선언한 FAST_FORWARD는 FORWARD_ONLY와 READ_ONLY 조합이 합쳐진 설정으로 빠르게 커서 작업을 수행하는 일반적으로 가장 많이 사용되는(결과셋을 로우 단위로, 시작부터 끝까지 빠르게 전진하면서 작업) 방식입니다.
STATIC, KEYSET 커서는 STATIC은 사용할 데이터를 tempdb에 복사하며, KEYSET은 행과 순서를 고정하고 일부 keyset 정보만 tempdb에 저장하는 방식입니다.
FAST_FORWARD가 가장 많이 사용됩니다. 하지만, 커서를 세밀하게 제어하고 싶은 경우에는 다른 방법으로 커서를 선언하세요.
이어지는 FOR SELECT 구문은 로우 단위 작업을 수행할 결과셋을 가져오는 SQL 구문을 작성합니다. 이때, 이렇게 컬럼명을 명시하고, 작업에 필요한 컬럼만 리스트 합니다.
커서 오픈
--커서 오픈 OPEN curMailTask
커서 오픈 명령을 수행하면, 선언된 SELECT 구문을 실행하고 첫 로우를 포인팅 합니다.
커서 FETCH
--변수 선언 DECLARE @mail_idx INT DECLARE @mail_num NVARCHAR(3) DECLARE @mail_address NVARCHAR(6) DECLARE @fetched_num NVARCHAR(3) --첫 로우 FETCH FETCH NEXT FROM curMailTask INTO @mail_idx, @mail_num, @mail_address
로우 단위 작업의 컬럼 값을 변수에 담아야 합니다. 개별 컬럼 값을 담을 변수를 선언하고 FETCH를 수행하면, 컬럼 값을 순차대로, FETCH INTO 구문 뒤의 변수에 넣습니다.
커서 작업 루틴
커서 내부에서 실제 작업을 수행하는 핵심 로직입니다.
WHILE @@FETCH_STATUS = 0 BEGIN -- FETCH 된 우편물의 우편 주소에 맞춰 우편번호 수정 SET @fetched_num = (SELECT mail_num FROM MailsNumer WHERE mail_address LIKE @mail_address) -- 만약 우편번호 테이블에서 가져온 우편번호와 우편물의 우편번호가 다르면 UPDATE 수행 IF @fetched_num <> @mail_num BEGIN UPDATE Mails SET mail_num = @fetched_num WHERE mail_idx = @mail_idx END --다음 로우 FETCH - 루프 FETCH NEXT FROM curMailTask INTO @mail_idx, @mail_num, @mail_address END
프로그래밍 언어의 작업과 마찬가지로 반복문 WHILE 구문과 같은, 커서의 모든 로우를 가져올 때까지 수행하는 @@FETCH_STATUS = 0 조건을 이용해 반복문을 수행합니다.
☑️ 챗GPT 활용: 프로그래밍 언어의 반복문 WHILE 구문에 대해서 알려줘
FETCH 할 때 수행되는 @@FETCH_STATUS 값은 아래와 같습니다.
@@FETCH_STATUS 값
반환 값
|
설명 |
0 | FETCH 구문 성공 |
-1 | FETCH 구문이 실패했거나 로우가 결과셋 범위를 벗어남 |
-2 | 반입된 로우가 없음 |
-9 | 커서가 FETCH 작업을 수행하지 않음 |
반복문은 BEGIN - END 블록 안에서 실행됩니다.
작업의 목표는 우편물의 주소를 “우편번호 테이블”의 주소와 비교해 다르면 UPDATE 해야 합니다. 위의 FETCH 문에 현재 로우의 주소 정보를 @mail_address에 넣었고, 우편번호 테이블에서 @mail_address로 검색해 우편번호를 가져와 @fetched_num 변수에 저장합니다.
우편물 테이블의 우편번호와 다르면, UPDATE 구문을 실행합니다. 업데이트할 값은 우편번호 테이블에서 가져온 우편번호인 @fetched_num이며, 업데이트할 우편물 테이블 로우의 고윳값은 @mail_idx 컬럼 값입니다.
위 작업 후, 다음 로우를 FETCH 하고 WHILE 반복문을 수행하면서 더 이상 가져올 로우가 없을 때까지 반복하게 됩니다.
커서 CLOSE
--커서 CLOSE CLOSE curMailTask;
커서 작업을 모든 결과셋에 대해 진행하면 커서를 닫습니다. 만약, CLOSE 후 다시 커서를 OPEN 하면 커서는 초기화되어 결과셋의 맨 첫 로우를 다시 포인팅 하게 됩니다.
커서 DEALLOCATE
--커서 DEALLOCATE DEALLOCATE curMailTask; GO
커서가 해제되어 사용하던 모든 커서 리소스가 해제됩니다.
이렇게 간략히 커서 구문을 확인했습니다. FETCH 작업으로 컬럼의 값을 변수에 담는 작업과, WHILE 블록의 작업이 중요한 부분입니다. 다음 강좌를 진행하기 전체 차근차근 코드를 살펴보시길 바랍니다.
SQL 강좌 책 구매
강좌가 도움이 되셨다면, 책으로 구매 가능합니다. 책 판매 수익금은 전액 코딩 교육 사회공헌 활동에 기부되며, 아래 링크에서 구매하시면 더 많은 금액이 기부됩니다.