최근 프로젝트에서 조건에 맞는 카테고리 중 랜덤으로 하나씩 상품을 뽑고, 조건에 맞는 나머지 상품을 추가로 추출하여 총 n개를 만드는 쿼리를 작성할 일이 있었다. 

단순히, union all 로 처리할 수 있지만, 중복 상품, 조건 필터링, MyBatis와의 연동 등을 고려하다 보니 쿼리가 점점 길어졌다. 

이 글에서는 Oracle에서 중복을 피하면서 효율적인 쿼리를 작성하고, MyBatis XML에서 공통 쿼리를 재활용하는 방법을 소개한다. 

 

처음 작성한 쿼리 

처음에 간단하게 각각 하나씩 뽑고 UNION ALL 로 붙이면 될 줄 알았다. 
하지만 이 방식은 조건에 맞는 데이터를 추출하면서 쿼리 중복과 중복된 결과 처리 문제가 발생했다.
반복되는 UNION ALL 쿼리를 효율적으로 관리하고, MyBatis에서 공통 SQL을 재사용하는 방법을 정리해봤다.

SELECT prd_no FROM (
SELECT prd_no FROM A WHERE category = 1 AND category_code = '01' ORDER BY DBMS_RANDOM.RANDOM
) WHERE ROWNUM = 1
UNION ALL
... (category 2, 3, 4)

 

문제 상황

불편한 점

  • prd_no 중복 발생 시 정확한 카운트 문제
  • 중복 쿼리 반복 -> 가독성 저하
  • 유지보수 어려움
  • MyBatis 에서 동일 SQL 을 2~3번 반복 사용함 

 

해결: 공통 SQL 분리 (<sql> 재사용)

같은 로직을 3번 박복하는 대신, <sql> 로 분리하여 한 번만 작성하고 재사용하니 훨씬 깔끔하고 유지보수도 쉬워졌다. 

Oracle

WITH sample AS (
-- 중복된 부분 미리 정의
)
SELECT DISTINCT prd_no FROM sample
UNION ALL ...

MyBatis 

<sql id="sample">
-- 카테고리별 랜덤 추출
</sql>
<select id="getPrdNoList" resultType="Long">
SELECT DISTINCT prd_no FROM (
<include refid="sample"/>
UNION ALL
SELECT prd_no FROM ...
)
</select>

 

배운 점 / 팁 정리

  • Oracle 에선 WITH, MyBatis 에선 <sql> + <include> 조합을 활용하자.
  • 중복된 prd_no 가 생길 수 있으니, DISTINCT, NOT IN 으로 필터링
  • n개를 맞추려면 실제 뽑힌 상품 개수를 기준으로 계산해야 함
  • 쿼리가 복잡해질수록 공통 쿼리 분리 습관이 필요하다.