[TIL] Real MySQL UNION vs UNION ALL

2025.03.23
·
7 min read

UNION 과 JOIN 차이점

JOIN

  • 여러 테이블에서 조건에 맞는 레코드를 찾아 컬럼을 확장함.

  • 고객 테이블과 주문 테이블 조회

    • 고객 이름 옆에 주문 내역이 추가로 붙어서 결과가 나옴

      4249

  • 특징

    • 인덱스 잘 활용하면, 데이터를 바로 가져올 수 있음

      • 임시 테이블 이 필요가 없어진다.

UNION

  • 여러 쿼리나 테이블의 결과를 모아서 레코드 를 확장

    4250
  • 특징

    • 임시 테이블 을 사용해서 결과를 가공해야 한다.

    • 인덱스를 아무리 잘써도 해당 과정을 피할 수 없다

UNION 종류

UNION ALL

  • 여러 결과 셋을 중복 여부 상관없이 그대로 합쳐서 반환 한다

  • 동작 방식

    1. 첫 번째 쿼리 결과를 가져온다

    2. 두 번째 쿼리 결과를 그 아래에 붙인다

    3. 끝 ㅋㅋ

  • 장점

    • 빠른 반환

      • 쿼리 실행 중에도 일부 결과(첫 10건 등.. ) 을 클라이언트가 받을 수있다.

    • 임시 테이블이 필요없다

UNION DISTINCT ( UNION )

  • 여러 결과 셋에서 중복된 레코드를 제거 한 뒤에 반환한다.

  • 동작 방식

    1. 모든 쿼리 결과를 가져온다

    2. 임시 테이블 을 만들어서 결과에 저장한다.

    3. 중복 제거한 뒤에 클라이언트에 반환한다.

  • 특징

    • 임시 테이블을 사용한다

      • 중복 제거를 위해서 모든 데이터를 정렬하고 비교해야 한다.

    • 느린 반환

      • 모든 처리가 끝나야 결과를 보낸다.

왜 UNION DISTINCT 가 느린가?

동작 과정

  1. 임시 테이블 생성

    1. 쿼리 결과와 똑같은 구조의 임시 테이블을 만든다

    2. 모든 컬럼을 포함한 유니크 인덱스 를 생성

  2. 중복 체크 + 삽입

    1. 결과셋의 각 레코드를 하나씩 확인한다

      1. SELECT 를 통해 임시 테이블에 동일한 레코드가 있는지 확인 한다.

      2. 없으면 INSERT 로 추가

      3. 있으면 무시한다.

  3. 결과 반환

    1. 모든 레코드 처리가 끝나는 경우 임시 테이블에서 데이터를 읽어서 클라이언트로 보낸다.

  4. 임시 테이블 삭제처리

    1. 작업이 끝나는 경우 임시테이블을 지운다.

중복 판단의 기준

  • PK 나 유니크 인덱스가 아니라, 모든 컬럼 값이 동일한지 비교한다.

  • 왜 PK 를 사용하지 않느냐고 한다면

    • UNION 의 경우 서로 다른 테이블이나 가공된 결과셋를 다루기에

    • 가져오는 데이터에 PK 가 없을 수 있음.

UNION DISTINCT성능 문제있음.

  • 컬럼 수가 많을 수록 느려진다..

    • 컬럼 5 vs 500개인 경우 중복 체크하는 경우, 비교해야 할 값이 100배가 늘어나게 된다.

    • 임시 테이블의 유니크 인덱스도 커져서 메모리와 CPU 의 부담이 증가한다.

  • 결론

    • UNION DISTINCT 는 꼭 필요한 경우가 아니라면 피하는 것이 좋다.

성능 테스트

mydb> select *
      from employees_comp4k
      union all
      select *
      from employees_comp8k
[2025-03-23 13:51:18] 6 s 690 ms (execution: 7 ms, fetching: 6 s 683 ms)에서 1부터 600,048개 행을 불러왔습니다
...

//

mydb> select *
      from employees_comp4k
      union
      distinct
      select *
      from employees_comp8k
[2025-03-23 13:52:32] 4 s 281 ms (execution: 964 ms, fetching: 3 s 317 ms)에서 1부터 300,024개 행을 불러왔습니다

  • 두 테이블이 완전 동일한 경우에는

    • union distinct 성능이 당연히 좋았다.

mydb> select *
      from salaries
      where 10000 <= salaries.emp_no
        and salaries.emp_no <= 200000
      union all
      select *
      from salaries
      where 200000 < salaries.emp_no
        and salaries.emp_no <= 300000
[2025-03-23 13:58:50] 18 s 393 ms (execution: 6 ms, fetching: 18 s 387 ms)에서 1부터 1,897,693개 행을 불러왔습니다
mydb> select *
      from salaries
      where 10000 <= salaries.emp_no
        and salaries.emp_no <= 200000
      union
      distinct
      select *
      from salaries
      where 200000 < salaries.emp_no
        and salaries.emp_no <= 300000
[2025-03-23 13:59:12] 21 s 969 ms (execution: 3 s 594 ms, fetching: 18 s 375 ms)에서 1부터 1,897,693개 행을 불러왔습니다
  • 중복이 없는 경우

    • UNION ALL 이 약 3.576초(16%) 더 빠르다.

  • 실행 시간으로는

    • UNION ALL 6ms

    • UNION DISTINCT 3,594ms

    • 으로서 UNION DISTINCT 가 실행 단계에서 훨씬 더 오래 걸린다.

      • 중복 제거(DISTINCT) 작업때문으로 보인다.

  • 페칭 시간은

    • 동일

UNION 쿼리 팁

  1. 불필요한 컬럼은 뺴고 들고와라!

  2. UNION ALL 은 왠만하면 사용하고 차라리 어플리케이션 단에서 중복 제거하는 것도 좋은 방법이다.

    1. UNION ALL 은 어플리케이션 서버에서 10개씩 끊어서 처리할 수 있다.

UNION 주의

  1. UNION 의 경우

    • UNION 으로만 사용하는 경우

    • UNION DISTINCT 라고 인식하기에

    • 꼭 UNION ALL 인 경우 다 써줘야한다!







- 컬렉션 아티클