[MySQL] Cursor 개념 정리 + 예제

🔥Log·2023년 7월 23일
1

MySQL

목록 보기
9/16

🧐 Cursor?


커서는 MySQL의 Stored Procedure에서 복수의 행에 어떤 쿼리들을 반복적으로 수행하고자 할 때, 사용하는 구문이다.

Java, Javascript에서 사용되는 .forEach() 또는 C언어의 포인터와 비슷한 기능을 한다고 볼 수 있다.

🤔 근데... 굳이 Cursor?

MySQL에서 복수의 데이터에 쿼리를 수행하고 싶다면, 굳이 커서를 쓸 필요없이 WHERE문에 원하는 조건을 줘서 해당 조건을 만족하는 행들에 쿼리를 수행하면 된다.
근데 왜 굳이 Cusor가 왜 필요한 것일까?

그 이유는 위에서도 이야기했듯이 .forEach와 같은 기능이 필요한 경우가 있기 때문이다.

예를 들어서, 사용자 테이블에서 사용자들을 모두 가져와서 각 행들을 돌면서 뭔가를 수행하고 싶은 경우에 Cusor를 사용할 수 있다.
이렇게 어떤 조건을 만족하는 데이터를 SELECT문으로 가져와서, 그 결과들에 대해서 반복문이 돌고 싶다면, Cursor를 사용하는 게 좋은 접근이 될 수 있다.

👀 Cusor의 사용법

Cusor는 일반적인 프로그래밍 언어와 비교했을 때, 조금 복잡한 사용법을 갖고 있고 아래와 같이 사용해야한다.

  1. DECLARE : SELECT한 결과를 Cursor로 선언
  2. DECLARE HANDLER : 반복 조건 선언
  3. OPEN : 커서 열기
  4. FETCH : 커서에서 현재 행 가져오기
  5. (필요한 쿼리 수행)
  6. CLOSE : 커서 닫기

자! 그럼 이제 좀 더 실제적인 예시로 Cusor를 다뤄보자 🔥


💡 예시 : 유저 로그 기능


👀 상황 설정

1. 모든 사용자를 조회한다.
2. 각 사용자의 이름과 나이를 합친 문자열을 로그 테이블에 기록한다.

우리가 만들 기능을 이렇게 설정해보도록 하겠다.

🔨 테이블 & 샘플 데이터 생성

INSERT INTO 
	`user` (`name`, `age`) 
VALUES 
	("Lebron", 20), 
	("Messi", 21),
	("Ronaldo", 22),
	("Xavi", 23),	
	("Harry", 24),
	("Rooney", 25);

먼저 위와 같이 user 테이블을 만들고, 샘플 데이터를 만들어주자.

그리고 이렇게 유저에 대한 로그를 기록할 수 있는 user_log테이블도 만들어주자.

💾 예제

CREATE DEFINER=`root`@`localhost` PROCEDURE `userLogger`()
BEGIN

  DECLARE _userId		BIGINT;
  DECLARE _userName	VARCHAR(255);
  DECLARE _userAge	INT;
  DECLARE done 		BOOLEAN DEFAULT FALSE; 
  DECLARE user_cursor CURSOR FOR SELECT * FROM `user`;

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; 

  OPEN user_cursor;

  sample_loop:LOOP

      IF done THEN
          LEAVE sample_loop;
      END IF;

      FETCH user_cursor INTO _userId, _userName, _userAge;

      SET @content = CONCAT(_userName, _userAge);
      INSERT INTO `user_log` (`content`, `user_id`) VALUES(@content, _userId);

  END LOOP;
  
  CLOSE user_cursor;

END

우리가 필요한 기능을 이렇게 userLogger라는 이름으로 만들어보았다. 아무 설명이 없으면 이해하기 힘드니까, 주석을 추가하고 프로시져의 실행부만 가져와보겠다. 😊

BEGIN

  ##########
  # 선언부
  ##########
  DECLARE _userId	BIGINT; # user의 id를 담을 변수
  DECLARE _userName	VARCHAR(255); # user의 이름을 담을 변수
  DECLARE _userAge	INT; # user의 나이를 담을 변수
  DECLARE done 		BOOLEAN DEFAULT FALSE; # 커서 종료 상태를 담을 변수
  DECLARE user_cursor CURSOR FOR SELECT * FROM `user`; # 커서

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; # 커서 종료 조건

  ##########
  # 커서 열기
  ##########
  OPEN user_cursor;

  ##########
  # 반복문
  ##########
  sample_loop:LOOP

      # 반복문 종료 쿼리
      IF done THEN
          LEAVE sample_loop;
      END IF;

      # 커서 FETCH
      FETCH user_cursor INTO _userId, _userName, _userAge;

      # FETCHING한 데이터를 갖고 필요한 쿼리 수행
      SET @content = CONCAT(_userName, _userAge);
      INSERT INTO `user_log` (`content`, `user_id`) VALUES(@content, _userId);

  END LOOP;
  
  ##########
  # 커서 닫기
  ##########
  CLOSE user_cursor;

END

짜잔~

이전 섹션들에서 설명한대로 변수 선언, 커서 선언등등의 정해진 순서대로 Stored Procedure를 작성한 모습이다.

이 SP를 실행하면 어떻게 될까? 바~로 실행해보겠다.

CALL userLogger();

위와 같이 userLogger SP를 호출하면 아래와 같은 결과를 얻을 수 있다.


☕ 마무리


사실 요즘 대부분의 DB관련 로직은 ORM이나 쿼리 빌더를 통해서 간편하게 처리해서, Cusor를 쓸 일이 거의 없을 가능성이 높다 ㅎㅎ

하지만, Stored Procedure를 쓰면서 조회한 데이터들에 대해서 반복문을 돌고 싶은 경우가 생긴다면 Cusor를 사용해서 깔끔하게 처리해보는 것도 좋을 것이다.

그럼 이번 글은 마치도록 하겠다 🔥


참고

2개의 댓글

comment-user-thumbnail
2023년 7월 23일

글 잘 봤습니다.

1개의 답글