본문 바로가기

Real MySQL 2권

[Real MySQL 2권] DISTINCT, LIMIT n

 

1) DISTINCT

- 특정 칼럼의 유니크한 값을 조회하려면 SELECT 쿼리에 DISTINCT를 사용한다.

  많은 사용자가 조인을 하는 경우 레코드가 중복해서 출력되는 것을 막기 위해 

  DISTINCT를 남용하는 경향이 있다.

  DISTINCT를 남용하는 것은 성능적인 문제도 있지만, 쿼리의 결과도 의도한 바와 달라질 수 있다. 

 

2)  LIMIT n 

- LIMIT은 쿼리 결과에서 지정된 순서에 위치한 레코드만 가져오고자 할 때 사용한다.

  우선 LIMIT이 사용된 예제 쿼리를 한 번 살펴보자.

mysql > SELECT * FROM employees
        WHERE emp_no BETWEEN 10001 AND 10010 
        ORDER BY first_name
        LIMIT 0, 5;

 

- 위의 쿼리는 다음과 같은 순서로 실행된다.

  1. employees 테이블에서 WHERE 절의 검색 조건에 일치하는 레코드를 전부 읽어 온다.

  2. 1번에서 읽어온 레코드를 first_name 칼럼값에 따라 정렬한다.

  3. 정렬된 결과에서 상위 5건만 사용자에게 반환한다. 

 

-  오라클의 ROWNUM에 익숙한 사용자에게는 조금 이상하겠지만, 

   MySQL의 LIMIT은 WHERE 조건이 아니기 때문에 

   항상 쿼리의 가장 마지막에 실행된다. 

 

- LIMIT의 중요한 특성은 LIMIT에서 필요한 레코드 건수만 준비되면 

  즉시 쿼리를 종료한다는 것이다.

  즉, 위의 쿼리에서 모든 레코드의 정렬이 완료되지 않았다고 하더라도

  상위 5건까지만 정렬되면 작업을 멈춘다. 

mysql> SELECT * FROM employees LIMIT 0, 10;
mysql> SELECT * FROM employees GROUP BY first_name LIMIT 0, 10;
mysql> SELECT DISTINCT first_name FROM employees LIMIT 0, 10;

mysql> SELECT * FROM employees
       WHERE emp_no BETWEEN 10001 AND 11000
       ORDER BY first_name
       LIMIT 0, 10;

 

- 첫 번째 쿼리는 LIMIT이 없을 때는 employees 테이블을 처음부터 끝까지 읽는 풀 테이블 스캔을 실행할 것이다.  

  하지만 LIMIT 조건이 있기 때문에 풀 테이블 스캔을 실행하면서 MySQL이 스토리지 엔진으로부터

  10개의 레코드를 읽어 들이는 순간 스토리지 엔진으로부터 읽기 작업을 멈춘다. 

  이렇게 정렬이나 그루핑 또는 DISTINCT가 없는 쿼리에서 LIMIT 조건을 사용하면

  쿼리가 상당히 빨리 끝날 수 있다. 

 

- 두 번째 쿼리는 GROUP BY가 있기 때문에 GROUP BY 처리가 완료되고 나서야 LIMIT 처리를 수행할 수 있다. 

  인덱스를 사용하지 못하는 GROUP BY는 그루핑과 정렬의 특성을 모두 가지고 있기 때문에  

  일단 GROUP BY 작업이 모두 완료돼야만 LIMIT을 수행할 수 있다.

  결국 LIMIT이 GROUP BY와 함께 사용되는 경우에는 LIMIT 절이 있더라도 

  실질적인 서버의 작업 내용을 크게 줄여주지는 못한다. 

 

- 세 번째 쿼리에서 사용한 DISTINCT는 정렬에 대한 요건이 없이 유니크한 그룹만 만들어 내면 된다.

  MySQL은 스토리지 엔진을 통해 풀 테이블 스캔 방식을 이용해 

  employee 테이블 레코드를 읽어 들임과 동시에  DISTINCT를 위한 중복 제거 작업을 진행한다. 

 이 작업을 반복적으로 처리하다가 유니크한 레코드가 LIMIT 건수만큼 채워지면 

  그 순간 쿼리를 멈춘다. 

 

- 네 번째 쿼리는 employees 테이블로부터 WHERE 조건절에 일치하는 레코드를 읽은 후

   first_name 칼럼의 값으로 정렬을 수행한다. 

   정렬을 수행하면서 필요한 10건이 완성되는 순간, 나머지 작업을 멈추고 결과를 사용자에게 반환한다.