SQL 서브쿼리 주의점 | 최고값 비교할 때 조건 범위를 맞춰야 하는 이유

SQL에서 서브쿼리로 최고값을 비교할 때 가장 흔한 실수는 바깥 쿼리의 조건 범위와 안쪽 서브쿼리의 조건 범위를 다르게 잡는 것이다. 이 문제는 문법 실수라기보다 비교 기준을 잘못 만드는 문제에 가깝다. MAX(), MIN(), AVG() 같은 집계 함수를 서브쿼리와 함께 쓸 때는 “무엇의 최고값인지”, “어떤 범위의 평균인지”를 먼저 맞춰야 한다.

예를 들어 “2015년 영화 중 최고 평점”을 찾고 싶다면, 바깥 쿼리만 2015년 조건을 가지면 안 된다. 서브쿼리 안에서도 같은 범위를 봐야 비교가 성립한다. 이 원리를 이해하지 못하면 결과가 비거나, 전혀 다른 데이터를 가져오는 쿼리가 된다.

서브쿼리에서 조건 범위가 중요한 이유

서브쿼리는 쿼리 안에서 비교 기준값을 만드는 역할을 자주 맡는다. 대표적인 패턴은 아래와 같다.

  • 최고값과 같은 행 찾기
  • 평균보다 큰 행 찾기
  • 특정 그룹의 대표값과 원본 데이터 비교하기

문제는 이 비교 기준값이 어디까지의 데이터에서 계산된 값인지가 항상 중요하다는 점이다.

예를 들어 아래 두 질문은 비슷해 보여도 기준 범위가 다르다.

  • 전체 영화 중 최고 평점은?
  • 2015년 영화 중 최고 평점은?

둘 다 MAX(imdb_score)를 쓰지만, 계산 대상이 다르기 때문에 결과도 달라진다.

즉 서브쿼리에서 중요한 건 MAX() 자체가 아니라 MAX를 어떤 범위에 적용했는지다.

바깥 쿼리 조건은 안쪽 쿼리가 자동으로 따라오지 않는다

이 부분이 핵심이다 👀

많이 헷갈리는 이유는 서브쿼리가 바깥 쿼리 안에 들어 있으니, 바깥 WHERE 조건도 같이 먹을 것처럼 느껴지기 때문이다. 하지만 서브쿼리는 별도의 쿼리다. 바깥 쿼리의 조건을 자동으로 상속받지 않는다.

즉 아래처럼 이해해야 한다.

  • 바깥 쿼리: 최종으로 보여줄 행을 고르는 조건
  • 안쪽 쿼리: 비교 기준값을 계산하는 조건

둘이 같은 범위를 봐야 할 때는, 각각 직접 조건을 써줘야 한다.

가장 흔한 실수 패턴

이 문제는 보통 아래 상황에서 자주 발생한다.

1) 바깥 쿼리만 좁히고 서브쿼리는 전체를 보는 경우

예를 들어 “2024년 매출 중 최고값”을 찾고 싶은데, 바깥 쿼리에는 year = 2024를 걸고 서브쿼리에는 아무 조건도 안 넣으면, 실제 비교 대상은 전체 기간 최고 매출이 된다.

2) 그룹 조건을 서브쿼리에 넣지 않는 경우

예를 들어 “카테고리 A의 최고 평점 상품”을 찾고 싶을 때, 바깥 쿼리에만 category = 'A'를 두고 서브쿼리는 전체 상품 최고 평점을 구하면 범위가 어긋난다.

3) JOIN 이후 필터만 보고 서브쿼리 범위를 놓치는 경우

JOIN이 붙으면 쿼리가 복잡해 보여서, 바깥 조건만 보고 “대충 맞겠지” 하고 넘어가기 쉽다. 그런데 비교 기준을 만드는 서브쿼리 범위가 틀리면 JOIN이 아무리 맞아도 결과는 어긋난다.

잘못된 쿼리와 올바른 쿼리 차이

예를 들어 “2015년 영화 중 최고 평점 영화”를 찾는다고 해보자.

잘못된 방식은 이런 구조다.

SELECT title, imdb_score
FROM titles
WHERE release_year = 2015
  AND type = 'MOVIE'
  AND imdb_score = (
      SELECT MAX(imdb_score)
      FROM titles
  );

이 쿼리는 바깥에서만 2015년 영화 조건을 걸고, 서브쿼리는 전체 titles 테이블의 최고 평점을 구한다.

즉 비교 기준이 “2015년 영화 중 최고점”이 아니라 전체 최고점이다.

올바른 방식은 이렇게 범위를 맞춘다.

SELECT title, imdb_score
FROM titles
WHERE release_year = 2015
  AND type = 'MOVIE'
  AND imdb_score = (
      SELECT MAX(imdb_score)
      FROM titles
      WHERE release_year = 2015
        AND type = 'MOVIE'
  );

이제야 바깥 쿼리와 안쪽 쿼리가 같은 범위를 본다.

즉 핵심은 바깥 조건을 서브쿼리에도 그대로 맞춰주는 것이다.

이 원리는 MAX만의 문제가 아니다

이 실수는 MAX()에서만 나오는 게 아니다.

아래 함수들도 전부 같은 원리로 봐야 한다.

  • MIN()
  • AVG()
  • COUNT()
  • SUM()

예를 들어 “2024년 주문의 평균 결제금액보다 큰 주문”을 찾을 때도, 서브쿼리의 평균이 전체 주문 평균이면 안 되고 2024년 주문 평균이어야 한다.

즉 서브쿼리 조건 범위 문제는 특정 함수 문제가 아니라,

집계 기준과 비교 대상 범위를 일치시키는 문제다.

JOIN이 같이 들어가면 더 주의해야 한다

실무에서는 보통 최고값만 가져오는 게 아니라, 그 데이터에 연결된 다른 정보도 함께 보고 싶어서 JOIN이 같이 들어간다. 예를 들어 영화 제목뿐 아니라 감독 이름까지 가져오려면 titles와 credits를 JOIN해야 한다.

이때도 순서는 같다.

  1. 먼저 어떤 범위의 최고값인지 정한다.
  2. 서브쿼리에서 그 범위의 기준값을 구한다.
  3. 바깥 쿼리에서 해당 행을 찾는다.
  4. 필요한 정보는 JOIN으로 붙인다.

즉 JOIN은 추가 정보를 가져오는 도구이고,

서브쿼리는 비교 기준을 만드는 도구다.

둘의 역할을 분리해서 보면 훨씬 덜 헷갈린다.

실전에서 조건 범위를 점검하는 방법

서브쿼리가 들어간 쿼리를 짤 때는 아래 순서로 확인하면 실수가 줄어든다.

첫 번째

내가 찾고 싶은 대상이 전체 데이터인지, 특정 범위인지 먼저 정한다.

예를 들면:

  • 전체 영화 중 최고점
  • 2015년 영화 중 최고점
  • 특정 카테고리 상품 중 최고점

질문 자체에 이미 범위가 들어있다.

두 번째

그 범위 조건이 바깥 쿼리에만 있는지, 서브쿼리에도 있는지 확인한다.

세 번째

서브쿼리만 따로 실행해본다.

예를 들어 아래처럼 서브쿼리만 먼저 돌려보면 현재 비교 기준이 정확한지 바로 확인할 수 있다.

SELECT MAX(imdb_score)
FROM titles
WHERE release_year = 2015
  AND type = 'MOVIE';

서브쿼리를 단독으로 먼저 검증하면, 바깥 쿼리까지 포함된 전체 쿼리에서 원인 찾기가 훨씬 쉬워진다.

이런 경우엔 특히 한 번 더 의심해야 한다

아래 같은 문장이 문제 조건에 들어 있으면, 서브쿼리 범위를 꼭 점검하는 게 좋다.

  • 특정 연도
  • 특정 카테고리
  • 특정 유형
  • 특정 지역
  • 특정 상태값

즉 조건이 하나라도 좁아지면,

**“서브쿼리도 그 범위를 보고 있나?”**를 먼저 확인해야 한다.

정리

서브쿼리에서 최고값을 비교할 때 중요한 건 집계 함수 문법이 아니라 조건 범위 일치다. 바깥 쿼리가 특정 연도, 특정 카테고리, 특정 유형을 대상으로 한다면, 비교 기준을 만드는 서브쿼리도 같은 범위를 봐야 한다. 그렇지 않으면 전체 데이터 기준 최고값과 비교하는 잘못된 쿼리가 된다.

즉 MAX() 서브쿼리 실수의 본질은 문법 오류가 아니라 비교 기준을 잘못 만든 것이다.

서브쿼리가 들어간 쿼리가 헷갈릴 때는 문장 하나만 기억하면 된다.

바깥 쿼리 조건은 안쪽 쿼리가 자동으로 따라오지 않는다.


서브쿼리로 최고값을 비교할 때는 MAX()보다 먼저 바깥 쿼리와 안쪽 쿼리가 같은 범위를 보는지부터 확인해야 한다.