커서는 SQL에서 데이터 집합을 행 단위로 순회하고 각 레코드에 대해 특정 작업을 수행할 수 있도록 해줍니다. 이는 프로그래밍에서의 반복과 유사합니다. SQL 단일 쿼리로는 불가능한 복잡한 단계별 로직에 유용합니다(예: 외부 상태 변경이 포함된 단계적 계산).
커서 예제:
DECLARE my_cursor CURSOR FOR SELECT id, balance FROM Accounts WHERE isActive = 1; OPEN my_cursor; DECLARE @id INT, @bal DECIMAL(10,2); FETCH NEXT FROM my_cursor INTO @id, @bal; WHILE @@FETCH_STATUS = 0 BEGIN UPDATE Accounts SET balance = @bal * 1.05 WHERE id = @id; FETCH NEXT FROM my_cursor INTO @id, @bal; END CLOSE my_cursor; DEALLOCATE my_cursor;
장점:
단점과 함정:
트리거 내부에서 커서를 사용할 수 있습니까? 이것이 데이터베이스 성능에 어떤 영향을 미칠 수 있습니까?
답변 및 예제: 트리거 내부에서 커서를 사용하는 것은 가능하지만 매우 권장되지 않습니다 — 각 DML 연산이 영향을 받는 각 행에 대해 커서를 실행할 수 있어 요청과 잠금이 폭발적으로 증가할 수 있습니다.
CREATE TRIGGER UpdateBalance ON Accounts AFTER INSERT AS DECLARE c CURSOR FOR SELECT id FROM inserted; -- 나쁨! OPEN c; -- ...
사례
프로젝트: 소매 주문 요약. 재고에서 결함이 발견되면 재고를 계산해야 하는 작업이 있었습니다 — 다음 배치는 수동으로 할인 업데이트를 통해 재계산이 필요합니다. 배치를 순회하는 데 커서를 사용했습니다. 나중에 여러 번 실행했을 때 스레드가 몇 시간 동안 잠겼고 서버의 부하가 기하급수적으로 증가했으며, 잠금 경합이 실패로 이어졌습니다.
사례
프로젝트: ERP, 데이터 마이그레이션. 수입 처리 절차에 커서를 통한 오류 처리를 추가했습니다. 500만 행에서 커서가 집합 기반 UPDATE + CASE를 사용한 유사한 배치 처리보다 40배 느리다는 것을 고려하지 않았습니다. 느린 마이그레이션으로 마감일이 미뤄졌습니다.
사례
프로젝트: 금융 회사의 청구. 자금 이동 테이블 업데이트에 새로운 집계 잔액을 계산하기 위해 커서를 추가했습니다. 프로덕션에서 "STOP THE WORLD"가 발생하여 한 행의 삽입조차도 많은 행에 대해 중첩 커서를 실행해야 했기 때문에 서비스 작업이 지연되었습니다.