TIL
  • Contents
  • Book
    • 도메인 주도 설계
      • 1. 동작하는 도메인 모델 만들기
    • 오브젝트
      • 데이터 중심 설계
      • 책임 중심 설계
      • 책임 할당을 위한 GRASP 패턴
      • 메시지와 인터페이스
      • 객체 분해
    • Effective Java
      • Item 7 - 다 쓴 객체 참조를 해제하라
      • Item 7 발표 내용
      • Item 13 - clone 재정의는 주의해서 진행하라
      • Item 13 발표 내용
      • Item 16 - public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
      • Item 16 발표 내용
      • Item 26 - 로 타입은 사용하지 말라
      • Item 28 - 배열보다는 리스트를 사용하라
      • Item 28 발표 내용
      • Item 29 - 이왕이면 제네릭 타입으로 만들라
      • Item 30 - 이왕이면 제네릭 메서드로 만들라
      • Item 31 - 한정적 와일드 카드를 사용해 API 유연성을 높이라
      • Item 35 - ordinal 메서드 대신 인스턴스 필드를 사용하라
      • Item 37 - ordinal 인덱싱 대신 EnumMap을 사용하라
      • Item 37 발표 내용
      • Item 43 - 람다보다는 메서드 참조를 사용하라
      • Item 43 발표 정리
      • Item 56 - 공개된 API 요소에는 항상 문서화 주석을 작성하라
      • Item 56 발표 정리
      • Item 62 - 다른 타입이 적절하다면 문자열 사용을 피하라
      • Item 62 발표 정리
      • Item 73 - 추상화 수준에 맞는 예외를 던지라
      • Item 83 - 지연 초기화는 신중히 사용하라
      • Item 83 발표 내용
      • Item 89 - 인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라
      • Item 89 발표 내용
    • 개발자를 위한 SQL 튜닝
      • SQL 쿼리 실습을 위한 DB 서버 구축
      • 인덱스 튜닝
      • 인덱스 스캔 튜닝
      • 인덱스 스캔 튜닝 실습
      • 인덱스 패스트 풀 스캔
      • 테이블 풀 스캔 튜닝
      • 조인 튜닝
      • 중첩 루프 조인 튜닝
      • 중첩 루프 조인 튜닝 실습
      • 해시 조인 튜닝
      • 해시 조인 튜닝 실습
      • 세미 조인 튜닝
      • 세미 조인 튜닝 실습
      • 아우터 조인
      • 함수 튜닝
      • 부분 범위 처리 튜닝
      • 파티셔닝 튜닝
      • 파티션 인덱스 튜닝
      • 병렬 처리 튜닝
  • Java
    • Design Pattern
      • Intro
      • Types of Design Patterns
      • Creational
        • Builder Pattern
        • Singleton Pattern
        • Prototype Pattern
        • Factory Pattern
        • Abstract Factory Pattern
      • Structural
        • Adapter Pattern
        • Bridge Pattern
        • Composite Pattern
        • Decorator Pattern
        • Facade Pattern
        • Flyweight Pattern
        • Proxy Pattern
      • Behavioural
        • Chain of Responsibility Pattern
        • Command Pattern
        • Interpreter Pattern
        • Iterator Pattern
        • Mediator Pattern
        • Memento Pattern
        • Observer Pattern
        • State Pattern
        • Strategy Pattern
        • Template Method Pattern
        • Visitor Pattern
    • Java
      • Cracking the Coding Interview
      • TDD, Clean Code with Java 11기
        • 자동차 레이싱
        • 로또
        • 사다리 타기
        • 볼링 게임 점수판
    • 궁금증
      • 자바 8 버전의 인터페이스와 추상클래스
      • 자바의 제네릭은 어떻게 이전 버전과 호환되는 걸까?
      • 스프링 MVC 기본 구조
      • 마샬링과 직렬화
      • 인터뷰 질문 모음
      • Code Coverage
  • Database
    • Database
      • SQL 레벨업
      • DB 스터디
        • DBMS
          • MySQL
        • INDEX
        • Join(Nested Loop, Hash)
        • Join(Semi, Outer)
        • Partial Range Processing
        • Function
        • Partitioning
        • Parallel Processing
  • Network
  • Architecture
    • Issue
      • Git Push Error
      • SonarLint Warning - assertThatExceptionOfType()
  • Infra
  • Spring
    • Spring JPA
      • 1. 데이터 모델링 및 연관관계 설정
      • 2. 최적화 내용
      • 3. Spring-Data-Jpa
      • 4. Query DSL
    • Spring Security
      • Intro
    • Spring Batch
      • 배치용 디비 설치
      • 배치 데이터 분석하기
      • 배치 프로세스 구상하기 및 성능 차이 확인하기
  • Issue
  • Tistory
    • Tistory Blog
  • Design High Performing Architectures
  • Design Resilient Architectures
  • Design Secure Applications And Architectures
  • Design Cost-Optimized Architectures
Powered by GitBook
On this page
  • Intro
  • 실습 테이블 생성 및 데이터 등록
  • 중첩 루프 조인 (테이블 풀 스캔)
  • 중첩 루프 조인 (인덱스 스캔 튜닝)

Was this helpful?

  1. Book
  2. 개발자를 위한 SQL 튜닝

중첩 루프 조인 튜닝 실습

중첩 루프 조인 튜닝의 실습

Previous중첩 루프 조인 튜닝Next해시 조인 튜닝

Last updated 3 years ago

Was this helpful?

내용에서 참고한 내용입니다.

Intro

  • 기반 데이터

    • 고객 정보 10만건 생성

    • 주문 정보가 100만건 생성

  • 주문일자 + 주문이름에 대한 복합 인덱스를 생성

  • 쿼리 테스트

    • LEADING 힌트로 고객 테이블을 Driving 테이블로 설정

    • USE_NL 힌트로 주문정보 테이블을 Driven 테이블로 설정

    • 조인 조건에 대한 인덱스가 없는 경우에 대한 테이블 풀 스캔 동작 테스트

  • 튜닝 쿼리 테스트

    • Outer 테이블에 CUST_NM 컬럼으로 구성된 인덱스 추가

      • LEADING 힌트를 통해 고객 정보 테이블을 Driving 테이블로 설정

      • INDEX 힌트를 통해 고객이름에 대한 인덱스 스캔하도록 설정

    • Inner 테이블에 CUST_ID + ORD_DT 컬럼으로 구성된 복합 인덱스 생성

      • USE_NL 힌트를 통해 중첩 루프 조인을 유도, 주문정보 테이블을 Inner 테이블로 지정

      • INDEX 힌트를 통해 고객 ID + 주문일자 인덱스를 스캔하도록 설정

    • Outer 테이블인 고객정보 테이블의 스캔 건수만큼 Inner 테이블의 주문정보 테이블을 스캔 시 효율적인 인덱스 스캔을 하도록 유도하여 성능 향상을 확인

실습 테이블 생성 및 데이터 등록

CREATE TABLE TB_CUST
(
    CUST_ID  VARCHAR2(10), -- 고객ID
    CUST_NM  VARCHAR2(50), -- 고객명
    BIRTH_DT VARCHAR2(8),  -- 생일
    SEX      VARCHAR2(2),  -- 성별
    PHONE_NO VARCHAR2(11), -- 폰번호
    JOIN_DT  VARCHAR2(8),  -- 가입일자
    INST_DTM DATE,         -- 입력일시
    INST_ID  VARCHAR2(50), -- 입력자ID
    UPDT_DTM DATE,         -- 수정일시
    UPDT_ID  VARCHAR2(50)  -- 수정자ID
);

CREATE TABLE TB_ORD
(
    ORD_NO   VARCHAR2(10),  -- 주문번호
    ORD_DT   VARCHAR2(8),   -- 주문일자
    ORD_NM   VARCHAR2(150), -- 주문이름
    ORD_AMT  NUMBER(15),    -- 주문금액
    PRDT_CD  VARCHAR2(6),   -- 상품코드
    SALE_GB  VARCHAR2(2),   -- 판매구분
    PAY_GB   VARCHAR2(2),   -- 결제구분
    CUST_ID  VARCHAR2(10),  -- 고객ID
    INST_DTM DATE,          -- 입력시간
    INST_ID  VARCHAR2(50),  -- 입력자ID
    UPDT_DTM DATE,          -- 수정시간
    UPDT_ID  VARCHAR2(5)    -- 수정자ID
);

INSERT INTO TB_CUST
SELECT LPAD(TO_CHAR(ROWNUM), 10, '0'),
       DBMS_RANDOM.STRING('U', 50),
       TO_CHAR(SYSDATE - TRUNC(DBMS_RANDOM.VALUE(3650, 36500)), 'YYYYMMDD'),
       LPAD(MOD(ROWNUM, 2), 2, '0'),
       LPAD(TO_CHAR(TRUNC(DBMS_RANDOM.VALUE(3650, 36500))), 11, '0'),
       TO_CHAR(SYSDATE - TRUNC(DBMS_RANDOM.VALUE(1, 365 * 3)), 'YYYYMMDD'),
       SYSDATE,
       'DBMSEXPERT',
       NULL,
       NULL
FROM DUAL
CONNECT BY LEVEL <= 100000;

-- [2021-07-13 16:14:10] 100,000 rows affected in 14 s 249 ms

COMMIT;

CREATE TABLE DUAL_10
(
    DUMMY VARCHAR2(1)
)
;
INSERT INTO DUAL_10
SELECT DUMMY
FROM DUAL
CONNECT BY LEVEL <= 10;

COMMIT;

ALTER TABLE TB_ORD
    NOLOGGING;

INSERT /*+ APPEND */ INTO TB_ORD --APPEND 힌트 사용
SELECT LPAD(TO_CHAR(ROWNUM), 10, '0'),
       TO_CHAR(SYSDATE - TRUNC(DBMS_RANDOM.VALUE(1, 3650)), 'YYYYMMDD'),
       DBMS_RANDOM.STRING('U', 150),
       TRUNC(DBMS_RANDOM.VALUE(1000, 100000)),
       LPAD(TO_CHAR(MOD(TRUNC(DBMS_RANDOM.VALUE(1, 1000)), 50)), 6, '0'),
       LPAD(TO_CHAR(MOD(TRUNC(DBMS_RANDOM.VALUE(1, 1000)), 3)), 2, '0'),
       LPAD(TO_CHAR(MOD(TRUNC(DBMS_RANDOM.VALUE(1, 1000)), 10)), 2, '0'),
       CUST_ID,
       SYSDATE,
       'DBMSEXPERT',
       NULL,
       NULL
FROM TB_CUST,
     DUAL_10;

-- [2021-07-13 16:24:15] 1,000,000 rows affected in 4 m 18 s 672 ms

COMMIT;

ALTER TABLE TB_ORD
    ADD CONSTRAINT TB_ORD_PK
        PRIMARY KEY (ORD_NO);

ALTER TABLE TB_CUST
    ADD CONSTRAINT TB_CUST_PK
        PRIMARY KEY (CUST_ID);

ALTER TABLE TB_ORD
    ADD CONSTRAINT TB_ORD_FK
        FOREIGN KEY (CUST_ID) REFERENCES TB_CUST (CUST_ID);
  • 인덱스 구성

    • 주문 정보 테이블에 '주문일자 + 주문이름'으로 구성된 복합 인덱스를 생성

CREATE INDEX TB_ORD_IDX01 ON TB_ORD (ORD_DT, ORD_NM);
  • 통계 정보

ANALYZE TABLE TB_CUST COMPUTE STATISTICS
    FOR TABLE FOR ALL INDEXES FOR ALL INDEXED COLUMNS SIZE 254;

ANALYZE TABLE TB_ORD COMPUTE STATISTICS
    FOR TABLE FOR ALL INDEXES FOR ALL INDEXED COLUMNS SIZE 254;

중첩 루프 조인 (테이블 풀 스캔)

SELECT /*+ LEADING(A) USE_NL(B) */
    *
FROM TB_CUST A,
     TB_ORD B
WHERE A.CUST_NM LIKE 'L%'
  AND A.CUST_ID = B.CUST_ID
  AND B.ORD_DT
    BETWEEN TO_CHAR(SYSDATE - 365, 'YYYYMMDD')
    AND TO_CHAR(SYSDATE, 'YYYYMMDD');

-- [2021-07-29 15:55:39] 3,678 rows retrieved starting from 1 in 9 m 40 s 31 ms (execution: 15 s 947 ms, fetching: 9 m 24 s 84 ms)
  • SQL 분석

    • LEADING 힌트를 사용하여 고객 테이블을 Outer 테이블로 설정

    • USE_NL 힌트를 사용하여 주문정보 테이블과 중첩 루프 조인이 이루어지도록 설정

    • 고객테이블과 주문정보 테이블을 고객아이디 컬럼을 기준으로 '=' 조인

    • 주문정보 테이블은 고객아이디를 선두 컬럼으로 한 인덱스가 존재하지 않으므로 주문정보 테이블은 테이블 풀 스캔으로 처리된다.

  • SQL의 문제점

    • 주문정보 테이블에 고객 아이디를 선두 컬럼으로 구성된 인덱스가 존재하지 않는다.

    • 고객 정보 테이블의 결과 집합의 건수만큼 주문정보 테이블에 테이블 풀 스캔을 하게 된다.

  • Execute plan

    • TB_CUST 테이블을 테이블 풀 스캔한다.

    • TB_ORD 테이블을 테이블 풀 스캔한다.

    • TB_CUST 테이블과 TB_ORD 테이블을 중첩 루프 조인(NESTED LOOPS)한다.

      • 즉, TB_CUST 결과 집합의 건수만큼 TB_ORD 테이블을 테이블 풀 스캔하게 된다.

    • 조건 절에 조건에 따른 필터링 처리를 한다.

    • SELECT 절의 연산을 수행

중첩 루프 조인 (인덱스 스캔 튜닝)

CREATE INDEX TB_CUST_IDX01 ON TB_CUST (CUST_NM);
CREATE INDEX TB_ORD_IDX02 ON TB_ORD (CUST_ID, ORD_DT);

ANALYZE INDEX TB_CUST_IDX01 COMPUTE STATISTICS;
ANALYZE INDEX TB_ORD_IDX02 COMPUTE STATISTICS;

SELECT/*+ LEADING(A) INDEX(A TB_CUST_IDX01) 
        USE_NL(B) INDEX(B TB_ORD_IDX02)  */
    *
FROM TB_CUST A,
     TB_ORD B
WHERE A.CUST_NM LIKE 'L%'
  AND A.CUST_ID = B.CUST_ID
  AND B.ORD_DT
    BETWEEN TO_CHAR(SYSDATE - 365, 'YYYYMMDD')
    AND TO_CHAR(SYSDATE, 'YYYYMMDD');

-- [2021-07-29 15:56:37] 3,678 rows retrieved starting from 1 in 384 ms (execution: 19 ms, fetching: 365 ms)
  • SQL 분석

    • LEADING 힌트를 사용하여 고객 테이블을 Outer테이블로 지정

    • INDEX 힌트를 사용하여 고객명에 대한 인덱스를 스캔하도록 지정

    • USE_NL 힌트를 이용하여 중첩 루프 조인을 유도, 주문 테이블을 Inner 테이블로 지정

    • INDEX 힌트를 사용하여 고객아이디 + 주문일자 인덱스를 스캔

    • 고객 테이블의 스캔 건수만큼 Inner 테이블인 주문정보 테이블을 스캔 시 효율적인 인덱스 스캔으로 성능 향상을 예상할 수 있다.

개발자를 위한 오라클 SQL 튜닝
튜닝 전 테이블 풀 스캔 실행 계획
중첩 루프 조인 인덱스 스캔 실행 계획