ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Real MySQL [7-19] 쿼리 작성 및 최적화 - 서브쿼리 (2)
    MySQL 2017. 3. 31. 20:28



    본 게시물의 내용과 이미지는 도서 Real MySQL의 내용을 재구성하여 작성되었습니다. 저자, 출판사에 의해 저작권 문제 발생시 게시물이 비공개 될 수 있음을 알립니다. 

    - 포스팅에 포함된 Query 결과는 AWS EC2 FreeTier(CentOS7.2) 환경에 MySQL v5.7.17 에서 실행한 결과를 포함하였습니다. 실행환경에 따라 Query 실행시간이나 RDBMS 실행계획이 다를 수 있습니다. 

    - 샘플 코드 dump 파일 적용안내 : http://cafe.naver.com/realmysql 

    - 샘플 코드 dump 파일 경로 : http://github.com/wikibook/realmysql/archive/master.zip


    SELECT 절에 사용된 서브 쿼리


    SELECT 절에 사용된 서브 쿼리는 내부적으로 임시 테이블을 만들거나 쿼리를 비효율적으로 실행하도록 만들지 않기 때문에 서브 쿼리가 적절히 인덱스를 사용할 수 있다면 크게 주의할 사항은 없다. 


    SELECT 절에 서브 쿼리를 사용하면 그 서브 쿼리는 항상 칼럼과 레코드가 하나인 결과를 반환해야 한다. 그 값이 NULL이든 아니든 관계없이 레코드가 1건이 존재해야 한다는 것이지만 MySQL 에서는 이 체크 조건이 조금은 느슨하다. 








    위 예제의 각 쿼리에서 주의할 점은 다음과 같다


    - 첫 번째 쿼리에서 사용된 서브 쿼리는 항상 결과가 0건이다. 하지만 에러를 발생하지 않고, 서브 쿼리의 결과는 NULL로 채워져서 반환된다.


    - 두 번째 쿼리에서 서브 쿼리가 2건 이상 레코드를 반환하는 경우에는 에러가 나면서 쿼리가 종료된다.


    - 세 번째 쿼리와 같이 SELECT 절에 사용된 서브 쿼리가 2개 이상의 칼럼을 가져오려고 할 때도 에러가 발생한다. 즉  SELECT 절의 서브 쿼리에는 로우 서브 쿼리를 사용할 수 없고 오직 스칼라 서브 쿼리만 사용할 수 있다. 


    [참고]

    - 서브 쿼리는 결과에 따라 스칼라 서브쿼리와 레코드 서브쿼리(또는 Row subquery)로 구분할 수 있다. 스칼라 서브 쿼리는 레코드의 칼럼이 각각 하나인 결과를 만들어내는 서브 쿼리이며, 스칼라 서브 쿼리보다 레코드 건수가 많거나 칼럼 수가 많은 결과를 만들어내는 서브 쿼리를 레코드 서브 쿼리 또는 로우(Row) 서브 쿼리라고 한다. 



    가끔 조인으로 처리해도 되는 쿼리를 SELECT 절의 서브 쿼리를 사용해 작성할 때도 있다. 하지만 서브 쿼리로 실행될 때보다 조인으로 처리될 때가 훨씬 빠르기 때문에 가능하다면 조인으로 쿼리를 작성하는 것이 좋다. 






    위의 두 예제 쿼리 모두 employees 테이블을 두 번씩 프라이머리 키를 이용해 참조하는 쿼리다. 물론 위 emp_no는 프라이머리 키라서 조인이나 서브 쿼리 중 어떤 방식을 사용해도 같은 결과를 가져온다. 첫 번재 쿼리와 두번째 쿼리의 응답속도의 차이가 있다. 처리해야 하는 레코드 건수가 많아지면 많아질수록 성능 차이가 커지므로 가능하다면 조인으로 쿼리를 작성하는 것이 좋다.




    WHERE 절에 단순 비교를 위해 사용된 서브 쿼리


    서비쿼리가 WHERE 절에서 사용될 때 서브 쿼리는 범위 제한 조건으로 사용되지 못한다, 이는 MySQL을 포함한 일반적인 RDBMS에서 똑같다. 독립 서브 쿼리일 때는 서브 쿼리를 먼저 실행한 후 상수로 변환하고, 그 조건을 범위 제한 조건으로 사용하는 것이 일반적인 RDBMS의 처리 방식이다. 하지만 MySQL은 독립 서브 쿼리를 처리하는 방식이 조금 다르다. 




    실행계획의 첫 번째 라인에서 dept_emp 테이블을 위해 ix_empno_fromdate 인덱스를 필요한 부분만 레인지 스캔으로 읽었다. 두 번재 라인의 서브 쿼리가 먼저 실행되어 그 결과를 외부 쿼리 비교 조건의 입력으로 전달했다. 




    WHERE 절에 IN과 함께 사용된 서브 쿼리 - IN (subquery)


    쿼리의 WHERE 절에 IN 연산자를 상수와 함께 사용할 때는 동등 비교와 똑같이 처리되기 때문에 상당히 최적화돼서 실행된다.




    IN의 입력으로 상수가 아니라 서브 쿼리를 사용하면 쿼리가 비효율적이다 라고 책에 나와있지만 MySQL 5.7 에서 실행된 결과로는 그렇지 않고 아래와 같이 최적화 된다.






    FROM 절에 사용된 서브 쿼리


    쿼리를 튜닝할 때는 가장 먼저 FROM 절의 서브 쿼리를 조인 쿼리로 바꾼다. FROM 절에 사용된 서브 쿼리는 항상 임시 테이블을 사용하므로 제대로 최적화되지 못하고 비효율적일 때가 많다.


    FROM 절에 사용된 서브 쿼리의 최적화가 얼마나 부족한지 간단한 예제로 살펴보자. 


    라고 하였으나..


    MySQL 5.7에서는 임시 테이블을 사용하지 않는 .. 최적화된 실행계획으로 실행되어 예제를 생략한다.



Designed by Tistory.