DB

[MySQL] UPDATE + 서브쿼리 완전 정복 – 조건 기반 동적 수정의 핵심 기술

인생아 2025. 6. 30. 21:49
반응형

MySQL에서 UPDATE 문은 단순한 정적 데이터 수정에 그치지 않는다.
서브쿼리(Subquery)를 활용하면 다른 테이블의 계산 결과나 조건을 기반으로 동적으로 값을 수정할 수 있다.
이는 실무에서 집계 기반 수정, 참조 기반 갱신, 조건부 연산 등을 구현할 때 매우 강력한 도구가 된다.

✅ 서브쿼리를 이용한 UPDATE 기본 문법

UPDATE 테이블명
SET 컬럼명 = (SELECT 컬럼 FROM 다른테이블 WHERE 조건)
WHERE 조건;

SET 구문에서 괄호로 감싼 SELECT 문이 서브쿼리이다.
해당 서브쿼리는 한 개의 값을 반환해야 하며, 복수 행을 반환하면 오류가 발생한다.

반응형

✅ 기본 예제: 회원별 총 주문 수 갱신

UPDATE users
SET total_orders = (
  SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id
);

이 예제는 orders 테이블에서 사용자별 주문 수를 집계하여, users 테이블의 total_orders 필드를 동기화한다.
다른 테이블의 통계 데이터를 기준으로 업데이트하는 대표적인 활용 사례이다.

✅ 다중 서브쿼리 예제

UPDATE users
SET total_spent = (
    SELECT SUM(total_price)
    FROM orders
    WHERE orders.user_id = users.id
),
    last_order_at = (
    SELECT MAX(order_date)
    FROM orders
    WHERE orders.user_id = users.id
);

이 쿼리는 사용자의 총 지출 금액과 마지막 주문일을 각각 계산해서 users 테이블에 반영한다.
두 개 이상의 서브쿼리를 조합하여 다중 컬럼 업데이트도 가능하다.

✅ WHERE 조건에도 서브쿼리 사용 가능

UPDATE products
SET discount = 0
WHERE category_id IN (
  SELECT id FROM categories WHERE type = '단종'
);

이 쿼리는 categories 테이블에서 type = '단종'인 항목만 필터링하여, 해당 카테고리에 속한 상품들의 할인율을 초기화한다.
WHERE 절에 사용하는 서브쿼리는 대상 범위를 동적으로 지정할 때 매우 유용하다.

✅ EXISTS를 활용한 조건 기반 UPDATE

UPDATE members m
SET status = '활성'
WHERE EXISTS (
  SELECT 1 FROM logins l
  WHERE l.member_id = m.id AND l.login_at >= CURDATE() - INTERVAL 30 DAY
);

최근 30일 이내 로그인 기록이 있는 회원만 활성 상태로 전환하는 예제이다.
EXISTS는 특정 조건을 만족하는 데이터가 존재하는지만 판단하므로, 성능과 로직 제어에 효과적이다.

반응형

✅ 서브쿼리 + JOIN 조합 불가할 때의 대안

MySQL에서는 UPDATE JOIN과 UPDATE + 서브쿼리는 둘 다 지원되지만, 복잡한 다중 JOIN을 서브쿼리 안에서 사용하면 성능 저하 또는 오류가 발생할 수 있다.

이럴 때는 뷰(VIEW)를 만들거나, 서브쿼리를 별도 테이블로 분리해서 처리하는 방법이 있다.

CREATE TEMPORARY TABLE recent_orders AS
SELECT user_id, MAX(order_date) AS last_order
FROM orders
GROUP BY user_id;

UPDATE users u
JOIN recent_orders ro ON u.id = ro.user_id
SET u.last_order_at = ro.last_order;

서브쿼리를 임시 테이블로 분리한 후 JOIN으로 처리하면 복잡한 쿼리도 안정적으로 관리할 수 있다.

✅ 다형성 조건 처리 (CASE + 서브쿼리)

UPDATE users
SET grade = (
  SELECT CASE
    WHEN SUM(total_price) >= 100000 THEN 'VIP'
    WHEN SUM(total_price) >= 50000 THEN 'GOLD'
    ELSE 'SILVER'
  END
  FROM orders
  WHERE orders.user_id = users.id
);

이 쿼리는 사용자의 총 구매 금액에 따라 등급을 자동으로 분류하여 반영한다.
서브쿼리와 CASE 문을 조합하면 복잡한 조건 분기를 우아하게 처리할 수 있다.

✅ UPDATE 서브쿼리 사용 시 주의사항

항목 설명
단일 값만 반환 가능 서브쿼리는 하나의 스칼라 값만 반환해야 한다
다중 행 반환 시 오류 발생 Subquery returns more than 1 row 에러 발생 가능
성능 고려 WHERE 조건 없이 전체 행 반복 조회 시 성능 저하
인덱스 적극 활용 서브쿼리 내부도 인덱스를 활용할 수 있어야 빠르다

항상 SELECT로 서브쿼리 결과를 먼저 확인한 후 UPDATE에 적용하는 것이 안전하다.

✅ 공식 문서 참고 링크

반응형