실행계획은 우리가 실행한 SQL이 내부적으로 어떻게 처리되는지 알려줍니다. SQL 성능 개선을 위해서는 실행계획을 읽고 해석할 수 있어야 합니다. PG는 실행계획을 트리(Tree) 형태로 표현해 줍니다. 많은 DBMS가 이와 같은 트리 형태 실행계획을 제공합니다. MySQL 역시 8버젼 이상부터는 트리 형태로 실행계획을 제공합니다.

아래와 같이 SQL을 실행해보기 바랍니다.

EXPLAIN
/*+ NestLoop(t1 t2) */
SELECT  t1.* ,t2.ord_amt
FROM    ms_member t1 LEFT OUTER JOIN (
          SELECT  a.member_id, sum(a.ord_amt) ord_amt
          FROM    tr_ord a
          GROUP BY a.member_id
          ) t2
          ON (t2.member_id = t1.member_id)
WHERE   t1.join_dtm = TO_DATE('20190324','YYYYMMDD');

위 SQL을 실행하면 다음과 같은 실행계획이 출력됩니다.

1. Nested Loop Left Join  (cost=13510.51..14625.29 rows=17 width=99)
2.   Join Filter: ((t2.member_id)::text = (t1.member_id)::text)
3.   ->  Seq Scan on ms_member t1  (cost=0.00..271.99 rows=17 width=67)
4.         Filter: (join_dtm = to_date('20190324'::text, 'YYYYMMDD'::text))
5.   ->  Materialize  (cost=13510.51..13593.28 rows=3010 width=38)
6.         ->  Subquery Scan on t2  (cost=13510.51..13578.23 rows=3010 width=38)
7.               ->  HashAggregate  (cost=13510.51..13548.13 rows=3010 width=38)
8.                     Group Key: a.member_id
9.                     ->  Seq Scan on tr_ord a  (cost=0.00..10948.34 rows=512434 width=11)

설명 편의를 위해 실행계획의 각 라인 별로 번호를 표시했습니다. 트리 형태의 실행계획을 구성하는 각 단계 간에는 선후 관계의 순서가 존재합니다. 다만, 이러한 순서를 100% 정확히 해석하기는 어렵습니다. 또한 SQL 성능 개선을 위해 실행계획의 각 단계별 처리 순서를 100% 정확히 파악해야 할 필요도 없습니다. 실행계획의 중요 부분에 대한 처리 흐름을 이해하고 비효율을 찾아 낼 수 있으면 충분히 성능 개선을 할 수 있습니다.

트리 형태를 구성하는 하나의 단계는 자신을 기준으로 부모나 자식, 또는 형제를 가질 수 있습니다. 자신보다 상위 단계가 부모가 되며, 자신의 하위 단계가 자식이 됩니다. 그리고 자신과 같은 레벨이며, 부모가 같은 단계가 형제 단계가 됩니다. 형제 단계의 경우는 자신보다 위쪽에 표시되면 형이 되고 아래쪽이 표시되면 동생이 됩니다. 이와 같은 기준으로 실행계획의 몇 가지 단계를 정리해보면 다음과 같습니다. 위의 실행계획과 아래 설명을 같이 살펴보기 바랍니다.

일반적인 실행계획의 처리 순서 규칙은 부모보다는 자식 먼저, 형제 간에는 형 먼저입니다. 하지만 이러한 순서가 100% 정확한 것은 아닙니다. 상황(단계별 처리 내용과 DBMS)에 따라서 실질적인 처리 순서는 완전히 달라질 수 있습니다. 실행계획의 순서를 해석하기 위해 이 규칙을 참고할 수 있으나, 정확할 수 없다는 점을 명심하기 바랍니다. 이제 지금까지 살펴본 내용을 종합해 실행계획의 큰 줄기를 정리해보면 다음과 같습니다.