DB - SQL 튜닝(postgreSQL)

doohyunlm·2021년 9월 13일
19

SQL, DB

목록 보기
5/5
post-thumbnail

목차

1. 인덱스를 타지 않는 경우
2. IN vs EXIST
3. SQL에 Alias가 없는 경우
4. NULL을 처리해주지 않은 경우
5. JOIN의 순서를 생각하지 않은 경우
6. 마치며


  • SQL을 작성하고 원하는 결과값을 받는다고 해서 그것이 끝은 아닙니다.

  • 고객이 몰려서 속도가 느린 쿼리가 될수도 있고 유지보수 측면에서 이거저거 추가하다보면 성능이 저하되 느린 쿼리들이 생겨나게 됩니다.

  • 그렇기에 저희는 리펙토링을 통해 시간을 줄이고 SQL을 튜닝하여 속도를 보장해줘야합니다.

  • SQL 튜닝에 대해서 같이 알아보겠습니다.




1. 인덱스를 타지 않는 경우


  • 인덱스를 타지 않는 경우는 대부분 좌변에 가공을 하는 경우에 발생합니다.

  • 좌변에 가공을 하는 경우는 TO_CHAR()같은 내장 함수로 감싸 사용하는 경우가 대부분입니다.

  • 인덱스를 타지 않게되면 성능 저하가 많이 되기 때문에 체크해서 수정해주어야 합니다.

SELECT
	*
FROM
	TB_USER
WHERE
	TRIM(USER_NO) = '0000001'
  • 위 경우 좌변에 가공이 들어가게 되어 인덱스를 타지 않는 SQL문으로
SELECT
	*
FROM
	TB_USER
WHERE
	USER_NO = '0000001'
  • 이러한 형식으로 변경해서 인덱스를 타게 수정하여 줍니다.
SELECT
	*
FROM
	TB_USER
WHERE
	USER_NO = 0000001
  • 위 경우처럼 좌변에 묵시적 형변환이 들어가게 되는 경우도 인덱스를 타지 않습니다.

  • ''를 넣어 우변에 형변환을 걸어주어 형변환이 일어나지 않게 해주어야 인덱스를 타게 됩니다.


결론

  • WHERE절에서 좌변 가공은 금지하며, 상수와 우변을 가공할 방법을 모색해야 합니다.

  • 칼럼이나 조건 값의 데이터 형 변환이 필요한 경우 명시적 형 변환을 사용해야 합니다.

명시적 형 변환이란?
좌변과 우변의 형을 맞춰주는 과정입니다.
예를 들어 좌변의 형식이 날짜 값이면 우변에서 TO_DATE함수를 활용해서 2가지의 타입을 맞춰주는 과정입니다.




2. IN vs EXISTS


  • INEXIST 연산은 각 값을 찾는데 있어서 해당하는 값이 있는지에 대해 확인하는 연산입니다.

  • 2개의 가장 큰 차이점은 IN모든 집합에서 충족한 집합을 찾아내는 과정을 거치지만 EXIST의 경우 명시된 기준을 충족하는 단일행의 조건을 찾아 검색하므로 성능 상 유리합니다.

IN

SELECT
	*
FROM
	TB_ORDER A
WHERE
	A.ORDER_DT BETWEEN TO_DATE('20210101' || '000000', 'YYYMMDDHH24MISS')
    		       AND TO_DATE('20210101' || '235959', 'YYYMMDDHH24MISS')
	AND A.USER_NO IN (
    			SELECT
                		X.USER_ID
                	FROM
                    		TB_USER X
               	)

EXISTS

SELECT
	*
FROM
	TB_ORDER A
WHERE
	A.ORDER_DT BETWEEN TO_DATE('20210101' || '000000', 'YYYMMDDHH24MISS')
    		       AND TO_DATE('20210101' || '235959', 'YYYMMDDHH24MISS')
	AND EXISTS (
    			SELECT
                		1
                	FROM
                    		TB_USER X
                    	WHERE
                        	X.USER_ID = A.USER_ID
               	)

결론

  • 쿼리 속도가 느릴 경우 IN절로 되어 있는 부분을 EXISTS로 변경하게 되면 속도가 빨라질수 있습니다.

  • 옵티마이저의 연산 속도가 올라가기 때문입니다.

옵티마이저란?
옵티마이저는 가장 효율적인 방법으로 SQL을 수행할 최적의 처리 경로를 생성해주는 DBMS의 핵심 엔진




3. SQL에 Alias가 없는 경우


  • SQL에서 Alias는 상당히 중요합니다.

  • Alias가 없는 경우 SQL문을 파싱할때 부하가 생기거나 옵티마이저가 실수를 할 수도 있기에 명시적으로 적어주는 것이 좋습니다.

  • Alias작성규칙의 경우 이 포스팅을 참고하세요.

2개이상의 테이블을 조인했을때 파싱과정에서 칼럼명이 겹치는 경우 에러와 부하가 오기때문에 명시적으로 Alias를
작성해주어야 합니다.

X

SELECT
	*
FROM
	TB_ORDER
WHERE
	ORDER_DT BETWEEN TO_DATE('20210101' || '000000', 'YYYMMDDHH24MISS')
    		     AND TO_DATE('20210101' || '235959', 'YYYMMDDHH24MISS')

O

SELECT
	*
FROM
	TB_ORDER A
WHERE
	A.ORDER_DT BETWEEN TO_DATE('20210101' || '000000', 'YYYMMDDHH24MISS')
    		       AND TO_DATE('20210101' || '235959', 'YYYMMDDHH24MISS')

결론

  • 유지보수 측면에서도 Alias가 있는 경우가 명시적으로 알아보기 쉽기 때문에 작성할때 습관을 들여서 작성하는 것이 좋습니다.




4. NULL을 처리해주지 않은 경우


  • 연산에 있어 NULL처리는 정말 중요합니다.

  • 잘못된 결과 값이 도출될 수 있기 때문입니다.

  • NULL과 값을 더하면 NULL이 출력됩니다.

SELECT
	SAL AS 연봉
    	, INCENTIVE AS 인센티브
        , SAL + INCENTIVE AS 총연봉
FROM
	TB_EMP
WHERE
	JOIN_DT = '20210101'
  • SQL문에서 인센티브가 없다면 총연봉 역시 NULL로 출력됩니다.

  • COALESCE함수를 통해 NULL값을 0으로 변경해주게 되면 정상 출력됩니다.

SELECT
	SAL AS 연봉
    	, INCENTIVE AS 인센티브
        , SAL + COALESCE(INCENTIVE,0) AS 총연봉
FROM
	TB_EMP
WHERE
	JOIN_DT = '20210101'

NULL인식

  • NULL은 값으로 인식이 불가능합니다.

  • 다른 칼럼에 하는 것처럼 사용하면 SQL에서 인식이 불가능합니다.

X

SELECT
	SAL AS 연봉
    	, INCENTIVE AS 인센티브
        , SAL + COALESCE(INCENTIVE,0) AS 총연봉
FROM
	TB_EMP
WHERE
	JOIN_DT = '20210101' OR JOIN_DT = NULL

O

SELECT
	SAL AS 연봉
    	, INCENTIVE AS 인센티브
        , SAL + COALESCE(INCENTIVE,0) AS 총연봉
FROM
	TB_EMP
WHERE
	JOIN_DT = '20210101' OR JOIN_DT IS NULL

결론

  • NULL이 포함된 칼럼에서 연산을 하거나 통계를 낼시에는 적절한 NULL처리가 필요합니다.




5. JOIN의 순서를 생각하지 않은 경우


  • JOIN은 작은 것부터 연산하는 것이 성능상 유리합니다.

  • JOIN을 3개이상 하게 되면 집합 값이 작은 것부터 읽는 것이 성능상 월등히 유리해집니다.

  • 초기에 잘 생각하여 3개이상의 테이블을 조인시 작은 집합이 먼저 읽히도록 앞에 써주고 연산합니다.

SELECT
	*
FROM
	TB_USER A
    	INNER JOIN TB_ORDER B
        ON A.USER_ID = B.USER_ID
WHERE
	B.ORDER_DT = '20210101'
  • SQL문에서 하나의 회원은 여러개의 주문을 할 수 있으므로 1대多관계입니다.

  • 작은 집합인 USER부터 써주고 큰 집합인 ORDER을 나중에 써주는 것이 좋습니다.


결론

  • SQL을 작성할때 ERD를 기반으로 해서 SQL을 작성하거나 관계에 대해 생각하면서 SQL문을 작성하도록 합시다.




6. 마치며


  • 회사를 다니면서 튜닝했던 정보들을 공유합니다.

  • 다른 분들에게도 많은 도움이 되었으면 좋겠습니다.

  • 틀린 부분이나 추가하면 좋겠을 규칙들도 적어주시면 감사하겠습니다.

profile
백엔드 개발자

7개의 댓글

comment-user-thumbnail
2021년 9월 14일

'Alias가 없는 경우 SQL문을 파싱할때 부하가 생기거나 옵티마이저가 실수를 할 수도 있다'
라고 하셨는데 혹시 어디에서 보셨던 내용인가요??

2개의 답글
comment-user-thumbnail
2021년 9월 14일

좋은 글 감사합니다. 아직 배운 게 적어서 별 생각 없이 쿼리를 짜고 있었는데, 생각보다 성능에 영향 주는 요소가 많았군요... 덕분에 많이 배웠습니다. :)

1개의 답글