7.3.4 SQL 주석


-- 이 표기법은 한 라인만 주석으로 처리합니다.

/* 이 표기법은 여러 라인을 
	주석으로 처리합니다. */
	
# 이 표기법도 MySQL에서는 한 라인만 주석으로 처리한다.
  • 유닉스 셸 스크립트에서처럼 “#”문자를 사용해서 한 라인을 주석으로 처리할 수 있다. “#”으로 주석을 표기하면 SQL 문장의 그 라인에서 “#” 문자 이후의 모든 내용은 주석으로 처리하기 때문에 “#” 문자를 여러 번 사용해도 무방하다.

7.4 SELECT


  • 웹 서비스 같이 일반적인 OLTP 환경의 데이터베이스에서는 INSERT나 UPDATE와 같은 작업은 거의 레코드 단위로 발생하므로 성능상 문제가 되는 경우는 별로 없다. 하지만 SELECT는 여러 개의 테이블로부터 데이터를 조합해서 빠르게 가져와야 하기 때문에 여러 개의 테이블을 어떻게 읽을 것인가에 많은 주의를 기울여야 한다. 하나의 애플리케이션에서 사용되는 쿼리 중에서도 SELECT 쿼리의 비율은 높다.

7.4.1 SELECT 각 절의 처리 순서

  • 절이란 우리가 주로 알고 있는 키워드(SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT)와 그 뒤에 기술된 표현식을 말한다.

  • -- 예제
      
    SELECT s.emp_no, COUNT(DISTINCT e.first_name) AS cnt
    FROM salaries s
    	INNER JOIN employees e ON e.emp_no=s.emp_no
    WHERE s.emp_no IN (100001, 100002)
    GROUP BY s.emp_no
    HAVING AVG(s.salary) > 1000
    ORDER BY AVG(s.salary)
    LIMIT 10;
      
    
    • SELECT 절 : SELECT s.emp_no, COUNT(DISTINCT e.first_name) AS cnt
    • FROM 절 : FROM salaries s INNER JOIN employees e ON e.emp_no=s.emp_no
    • WHERE 절 : WHERE s.emp_no IN (100001, 100002)
    • GROUP BY 절 : GROUP BY s.emp_no
    • HAVING 절 : HAVING AVG(s.salary) > 1000
    • ORDER BY 절 : ORDER BY AVG(s.salary)
    • LIMIT 절 : LIMIT 10
  • 위의 예제 쿼리는 SELECT 쿼리에 지정할 수 있는 대부분의 절을 포함하고 있다. 가끔 이런 쿼리에서 어느 절이 먼저 실행될지 예측하지 못할 때가 자주 있는데, 어느 절이 먼저 실행되는지를 모르면 처리 내용이나 처리 결과를 예측할 수 없다.

  • 실행 순서를 벗어나는 쿼리가 필요하다면 서브 쿼리로 작성된 인라인 뷰를 사용해야 한다. 예를 들어 쿼리에서 LIMIT를 먼저 적용하고 ORDER BY를 실행하고자 하면 다음과 같이 인라인 뷰를 사용해야 한다.

    • SELECT emp_no, first_name, max_salary
      FROM (
      	SELECT s.emp_no, COUNT(DISTINCT e.first_name) AS cnt
          FROM salaries s
          	INNER JOIN employees e ON e.emp_no=s.emp_no
          WHERE s.emp_no IN (100001, 100002)
          GROUP BY s.emp_no
          HAVING MAX(s.salary) > 1000
          LIMIT 10
      ) temp_view
      ORDER BY max_salary;
      
    • 만약 LIMIT를 GROUP BY 전에 실행하고자 할 때도 마찬가지로 서브 쿼리로 인라인 뷰를 만들어서 그 뷰 안에서 LIMIT를 적용하고 바깥 쿼리에서 GROUP BY와 ORDER BY를 적용해야 한다. 하지만 이렇게 인라인 뷰가 사용되면 임시 테이블이 사용되기 때문에 주의해야 한다. MySQL의 LIMIT는 오라클의 ROWNUM과 조금 성격이 달라서 WHERE 조건으로 사용하지 않고 모든 처리의 결과에 대해 레코드 건수를 제한하는 형태로 사용한다.

7.4.2 WHERE 절과 GROUP BY 절, 그리고 ORDER BY 절의 인덱스 사용

인덱스를 사용하기 위한 기본 규칙

  • WHERE 절이나 ORDER BY 또는 GROUP BY가 인덱스를 사용하려면 기본적으로 인덱스된 칼럼의 값 자체를 변환하지 않고 그대로 사용한다는 조건을 만족해야 한다. 인덱스는 칼럼의 값을 마우런 변환 없이 B-Tree에 정렬해서 저장한다. WHERE 조건이나 GROUp BY 또는 ORDER BY에서도 원본 값을 검색하거나 정렬할 때만 B-Tree에 정렬된 인덱스를 이용한다. 즉 인덱스는 salary 칼럼으로 만들어져 있는데, 다음 예제의 WHERE 절과 같이 salary 칼럼을 가공한 후 다른 상수 값과 비교한다면 이 쿼리는 인덱스를 적절히 이용하지 못하게 된다.
    • SELECT * FROM salaries WHERE salary*10 > 150000;
  • 저장하고자 하는 값의 타입에 맞춰서 칼럼의 타입을 선정하고, SQL을 작성할 때는 데이터의 타입에 맞줘서 비교 조건을 사용하길 권장한다.

WHERE 절의 인덱스 사용

  • WHERE 조건이 인덱스를 사용하는 방법은 크게 범위 제한 조건과 체크 조건으로 구분해 볼 수 있다.
  • 범위 제한 조건은 동등 비교 조건이나 IN으로 구성된 조건이 인덱스를 구성하는 칼럼과 얼마나 좌측부터 일치하는가에 따라 달라진다.

  • OR 연산자가 있으면 처리 방법이 완전히 바뀐다. WHERE절에서 각 조건이 AND로 연결되면 읽어와야 할 레코드의 건수를 줄이는 역할을 하지만 각 조건이 OR로 연결되면 읽어서 비교해야 할 레코드가 더 늘어나기 때문에 WHERE 조건에 OR 연산자가 있다면 주의해야 한다.

GROUP BY 절의 인덱스 사용

  • GROUP BY 절의 각 칼럼은 비교 연산자를 가지지 않으므로 범위 제한 조건이나 체크 조건과 같이 구분해서 생각할 필요 없다.
  • GROUP BY 절에 명시된 칼럼의 순서가 인덱스를 구성하는 칼럼의 순서와 같으면 GROUP BY 절은 인덱스를 이용할 수 있다.
    • GROUP BY 절에 명시된 칼럼이 인덱스 칼럼의 순서와 위치가 같아야 한다.
    • 인덱스를 구성하는 칼럼 중에서 뒷쪽에 있는 칼럼은 GROUp BY절에 명시되지 않아도 인덱스를 사용할 수 있지만 인덱스의 앞쪽에 있는 칼럼이 GROUP BY 절에 명시되지 않으면 인덱스를 사용할 수 없다.
    • WHERE 조건절과는 달리, GROUp BY 절에 명시된 칼럼이 하나라도 인덱스에 없으면 GROUP BY 절은 전혀 인덱스를 이용하지 못한다.
-- GROUP BY 절이 인덱스를 사용할 수 없는 패턴
... GROUP BY COL2, COL1 
... GROUP BY COL1, COL3, COL2
... GROUp BY COL1, COL3
... GROUP BY COL1, COL2, COL3, COL4, COL5
  • 첫 번째와 두 번째 예제는 GROUP BY 칼럼이 인덱스를 구성하는 칼럼의 순서와 일치하지 않기 때문에 사용하지 못하는 것
  • 세 번째 예제는 GROUP BY 절에 COL3가 명시됐지만 COL2가 그 앞에 명시되지 않았기 때문이다
  • 네 번째 예제에서는 GROUP BY 절의 마지막에 있는 COL5가 인덱스에는 없어서 인덱스를 사용하지 못하는 것이다.
-- GROUP BY 절이 인덱스를 사용할 수 있는 패턴
... GROUP BY COL1
... GROUP BY COL1, COL2
... GROUP BY COL1, COL2, COL3
... GROUP BY COL1, COL2, COL3, COL4
  • WHERE 조건절에 COL1이나 COL2가 동등 비교 조건으로 사용된다면, GROUP BY 절에 COL1이나 COL2가 빠져도 인덱스를 이용한 GROUP BY가 가능할 때도 있다.

ORDER BY 절의 인덱스 사용

  • MySQL에서 GROUP BY와 ORDER BY는 처리 방법이 상당히 비슷하다. 그래서 ORDER BY 절의 인덱스 사용 여부는 GROUP BY의 요건과 거의 흡사하다. 하지만 ORDER BY는 조건이 하나 더 있는데, 정렬되는 각 칼럼의 오름차순(ASC) 및 내림차순(DESC) 옵션이 인덱스와 같거나 또는 정바대의 경우에만 사용할 수 있다. 여기서 MySQL의 인덱스는 모든 칼럼이 오름차순으로만 정렬돼 있기 때문에 ORDER BY 절의 모든 칼럼이 오름차순이거나 내림차순일 때만 인덱스를 사용할 수 있다.

  • -- 인덱스를 사용하지 못하는 원인
    ... ORDER BY COL2, COL3
    ... ORDER BY COL1, COL3, COL2
    ... ORDER BY COL1, COL2 DESC, COL3
    ... ORDER BY COL1, COL3
    ... ORDER BY COL1, COL2, COL3, COL4, COL5
    
    • 첫 번째 에제는 인덱스의 제일 앞쪽 칼럼인 COL1이 ORDER BY 절에 명시되지 않았기 때문에 인덱스를 사용할 수 없다.
    • 두 번째 예제는 인덱스와 ORDER BY 절의 컬럼 순서가 일치하지 않기 때문에 인덱스를 사용할 수 없다.
    • 세 번째 예제는 ORDER BY 절의 다른 칼럼은 모두 오름차순인데, 두 번째 칼럼인 COL2의 정렬 순서가 내림차순이라서 인덱스를 사용할 수 없다,
    • 네 번째 예제는 인덱스에는 COL1과 COL3 사이에 COL2 칼럼이 있지만 ORDER BY 절에는 COL2 칼럼이 명시되지 않았기 때문에 인덱스를 사용할 수 없다.
    • 다섯 번째 예제는 인덱스에 존재하지 않는 COL5가 ORDER BY 절에 명시됐기 때문에 인덱스를 사용하지 못한다.

WHERE 조건과 ORDER BY(또는 GROUP BY) 절의 인덱스 사용

  • 일반적으로 우리가 사용하는 쿼리는 WHERE 절을 가지고 있으며, 선택적으로 ORDER BY나 GROUP BY 절을 포함할 것이다. 쿼리에 WHERE 절만 또는 GROUP BY나 ORDER BY 절만 포함돼 있다면 사용된 절 하나에만 초점을 맞춰서 인덱스를 사용할 수 있게 튜닝하면 된다.
  • SQL 문장이 WHERE 절과 ORDER BY 절을 가지고 있다고 가정했을 때 WHERE 조건은 A 인덱스를 사용하고 ORDER BY는 B 인덱스를 사용하는 것은 불가능하다. 이는 WHERE 절과 GROUP BY 절이 같이 사용된 경우와 GROUP BY와 ORDER BY가 같이 사용된 쿼리에서도 마찬가지다.
  • WHERE 절과 ORDER BY 절이 같이 사용된 하나의 쿼리 문장은 다음 3가지 중 한 가지 방법으로만 인덱스를 이용한다.
    • WHERE 절과 ORDER BY 절이 동시에 같은 인덱스를 이용
    • WHERE 절만 인덱스를 이용
    • ORDER BY 절만 인덱스를 이용
  • GROUP BY나 ORDER BY가 인덱스를 사용할 수 있을지 없을지 모호할 때는 이처럼 쿼리를 조금 변경한 쿼리와 원본 쿼리가 같은 순서나 결과를 보장하는지 확인해 보면 된다.
SELECT * FROM tb_test WHERE col1 > 10 ORDER BY col1, col2, col3;
SELECT * FROM tb_test WHERE COL1 > 10 ORDER BY col2, col3;
  • 위의 첫 번째 예제 쿼리에서 col1>10 조건을 만족하는 col1 값은 여러 개일 수 있다. 하지만 ORDER BY 절에 col1부터 col3까지 순서대로 모두 명시됐기 때문에 인덱스를 사용해 WHERE 조건절과 ORDER BY 절을 처리할 수 있다.
  • 두 번째 쿼리에서는 WHERE 절에서 col1이 동등 조건이 아니라 범위 조건으로 검색됐는데, ORDER BY 절에는 col1이 명시되지 않았기 때문에 정렬할 때는 인덱스를 이용할 수 없다.
  • WHERE절과 GROUP BY 절의 조합도 모두 똑같은 기준이 적용된다.

GROUP BY 절과 ORDER BY 절의 인덱스 사용

  • GROUP BY와 ORDER BY 절이 동시에 사용된 쿼리에서 두 절이 모두 하나의 인덱스를 사용해서 처리되려면 GROUP BY 절에 명시된 칼럼과 ORDER BY에 명시된 칼럼이 순서와 내용이 모두 같아야 한다.
  • GROUP BY와 ORDER BY가 같이 사용된 쿼리에서는 둘 중 하나라도 인덱스를 이용할 수 없을 때는 둘다 인덱스를 사용하지 못한다. 즉 GROUP BY는 인덱스를 이용할 수 있지만 ORDER BY가 인덱스를 이용할 수 없을 때 이 쿼리의 GROUP BY와 ORDER BY절은 모두 인덱스를 이용하지 못한다. 물론 그 반대의 경우도 마찬가지다.
... GROUP BY COL1, COL2 ORDER BY COL2
... GROUP BY COL1, COL2 ORDER BY COL1, COL3
  • MySQL의 GROUP BY는 GROUp BY 칼럼에 대한 정렬까지 함께 수행하는 것이 기본 동작 방식이므로 GROUp BY와 ORDER BY 칼럼이 내용과 순서가 같은 쿼리에서는 ORDER BY 절을 생략해도 같은 결과를 얻게 된다.

WHERE 조건과 ORDER BY 절, 그리고 GROUP BY 절의 인덱스 사용

  • WHERE 절과 GROUp BY 절, 그리고 ORDER BY 절이 모두 포함된 쿼리가 인덱스를 사용하는지 판단하는 방법
    1. WHERE 절이 인덱스를 사용할 수 있는가?
    2. GROUP BY 절이 인덱스를 사용할 수 있는가?
    3. GROUP BY 절과 ORDER BY 절이 동시에 인덱스를 사용할 수 있는가?