문제의 역사:
표준 SQL은 본래 반복(루프, 분기, 직접 이동) 요소가 없는 선언적 언어로 설계되었으나, T-SQL/PLSQL과 같은 확장을 통해 WHILE, CASE, 심지어 GOTO의 모방과 같은 구문이 가능해졌습니다.
문제:
반복 작업(레코드 처리 루프)은 대량 데이터를 처리할 때 성능 저하를 초래하는 경우가 많으며, 이를 "집합(세트) 기반" 접근 방식으로 대체하지 않는다면 성능 부진을 초래할 수 있습니다. 분기, CASE, IF는 매우 편리하지만 지나치게 중첩될 경우 코드의 가독성과 예측 가능성이 떨어집니다.
해결책:
제어 구조의 사용은 배치(bulk/set) 처리로 대체할 수 없는 경우에만 정당화되어야 합니다! 복잡한 계산의 경우에는 작은 루프, 트리거 또는 CASE를 사용하는 것이 허용됩니다. 대량 처리의 경우에는 윈도우 함수나 서브쿼리를 사용하는 UPDATE가 더 좋습니다.
코드 예시 (T-SQL):
DECLARE @i INT = 1 WHILE (@i <= 5) BEGIN IF @i % 2 = 0 PRINT CONCAT('짝수: ', @i) ELSE PRINT CONCAT('홀수: ', @i) SET @i = @i + 1 END
CASE 표현식:
SELECT num, CASE WHEN num % 2 = 0 THEN 'even' ELSE 'odd' END AS parity FROM numbers
주요 특징:
CASE가 WHERE처럼 행 필터링에 사용될 수 있습니까?
아니요! CASE는 다른 값을 반환하지만 행을 필터링하지는 않습니다. CASE를 사용하여 필터링하려는 일반적인 실수가 발생하며 결과가 올바르지 않을 것입니다.
WHILE과 CURSOR는 어떤 차이가 있나요 — 동일한 건가요?
WHILE은 사용자 정의 변수 관리가 가능한 기본 루프입니다; CURSOR는 테이블의 레코드에 대해 작동하며, 행을 참조합니다. CURSOR는 자원 소모가 더 크며 대량 데이터에 대해 훨씬 느리게 작동하는 경우가 많습니다.
대량 처리에 대한 더 빠른 접근 방식은 WHILE 루프와 UPDATE인가, 하나의 집합 기반 UPDATE인가?
99%의 경우, 하나의 집합 기반 UPDATE(또는 INSERT)가 한 행씩 처리하는 루프보다 훨씬 빠릅니다(비교적 "유연해 보이더라도").
-- 잘못된 접근 DECLARE @id INT = 1 WHILE (@id <= 100000) BEGIN UPDATE t SET flag=1 WHERE id=@id SET @id = @id + 1 END -- 올바른 접근 UPDATE t SET flag=1 WHERE id BETWEEN 1 AND 100000
백만 주문 상태를 업데이트하기 위해 각 id에 대해 UPDATE 루프를 작성했습니다. 처리 시간은 ~8시간이 걸렸습니다. 중간에 실패할 경우, 모든 작업이 손실되어 수동으로 수리해야 했습니다.
장점:
단점:
집합 기반 UPDATE로 변경하여 단일 표현식으로 처리했습니다. 실행 시간이 6분으로 단축되었고, 처리 방식은 원자적입니다.
장점:
단점: