Subquery vs Join, INDEX

손우진·2021년 4월 10일
0

이슈

목록 보기
2/2

INDEX를 타지 않는 조건

  1. 인덱스 컬럼 절을 변형한 경우

    • 수식이나 함수 등으로 인덱스 컬럼 절을 변형하였을 경우
    • 반드시 함수나 수식을 사용해야 하는 경우에는 인덱스 컬럼 부분에 적용하지 말고, 여기에 대입되는 컬럼이나 상수부분에 적용해야 한다.
    SELECT column_name FROM table_name WHERE TO_CHAR(column_name, 'YYYYMMDD') = '20130909';
    SELECT column_name FROM table_name WHERE column_name = TO_DATE('20130909', 'YYYYMMDD');
    
    SELECT column_name FROM table_name WHERE column_name * 100 > 10000;
    SELECT column_name FROM table_name WHERE column_name = 10000 / 100; 
    
  2. 내부적으로 데이터 형 변환이 일어난 경우

    • 서로 대입되는 항목끼리 데이터 타입이 다르면 내부적인 형 변환에 의해 컬럼이 함수를 사용한 효과를 나타낸다.
    SELECT column_name FROM table_name WHERE column_name  = '20130909'; // DATE 타입의 column
    SELECT column_name FROM table_name WHERE column_name = TO_DATE('20130909', 'YYYYMMDD');
    SELECT column_name FROM table_name WHERE column_name  = 100; // 문자 타입의 column
    SELECT column_name FROM table_name WHERE column_name = '100';
    
  3. 조건절에 NULL 또는 NOT NULL을 사용하는 경우

    • 기본적으로 인덱스를 구성한 컬럼 값이 전부 NULL이라면 인덱스는 이런 값을 저장하지 않는다.
    • 따라서 NULL인 값이 많지 않아 인덱스를 통해 엑세스를 하고자 한다면 데이터 생성 시 디폴트로 0과 같이 데이터를 만들어주는 것이 좋다. 반대로, 만약 NOT NULL이 분석 대상이라면 해당 컬럼을 NULL 허용 컬럼으로 두는 것이 좋다.
    SELECT column_name FROM table_name WHERE column_name IS NULL;
    SELECT column_name FROM table_name WHERE column_name IS NOT NULL;
    
    SELECT column_name FROM table_name WHERE column_name > '';
    SELECT column_name FROM table_name WHERE column_name >= 0;
    
  4. 부정형으로 조건을 사용한 경우
    부정문은 인덱스를 활용하지 못한다.

    ```
    SELECT column_name FROM table_name WHERE column_name != 30;
    SELECT column_name FROM table_name WHERE column_name < 30 AND column_name > 30;

    테이블을 한 번 더 읽어 NOT EXISTS를 사용

    > SELECT column_name FROM table_name WHERE NOT EXISTS 
    (SELECT column_name FROM table_name WHERE column_name = 30);
  5. LIKE 연산자를 사용하였을 경우

    • LIKE 연산자를 이용하여 검색을 할 경우 %를 앞에 넣어 사용하게 되면 인덱스를 타지 않는다.
    • INSTR을 사용
    • INSTR('비교할 대상', '비교하고자하는 값', 비교를 시작할 위치, 검색된 결과의 순번);
    SELECT column_name FROM table_name WHERE column_name LIKE '%S%';
    SELECT column_name FROM table_name WHERE column_name LIKE 'S%';
    SELECT column_name  FROM table_name WHERE INSTR(column_name  , 'cmp_value') > 0; 
    
  6. OR 조건 사용

    SELECT column_name FROM table_name1 t1, table_name2 t2 
    WHERE 
    (t1.column_name1 = t2.column_name1 OR t1.column_name2 = t2.column_name2)
    AND t1.column_name3 = 'cmp_value';
    
    SELECT column_name FROM table_name1 t1, table_name2 t2 
    WHERE t1.column_name1 = t2.column_name1 AND t1.column_name3 = 'cmp_value'; 
    UNION ALL
    SELECT column_name FROM table_name1 t1, table_name2 t2
    WHERE t1.column_name2 = t2.column_name2 AND t1.column_name3 = 'cmp_value';
    

Subquery vs JOIN

MYSQL 5.6 기준 Subquery 최적화 조건

  • IN(subquery) 또는 = ANY(subquery) 형태
  • UNION 없는 단일 SELECT
  • 집계함수 와 HAVING 절을 가지지 말아야
  • 서브쿼리의 WHERE 조건이 외부쿼리의 다른 조건과 AND 로 연결
  • 조인을 사용한 UPDATE 나 DELETE 가 아니어야
  • 미리 수립된 실행계획을 사용하지 않는 경우(PreparedStatement 사용시 실행계획 재사용됨)
  • 외부쿼리와 서브쿼리가 실제 테이블 사용(가상 테이블 사용시 세* 미조인 최적화 안됨)
  • 외부쿼리와 서브쿼리가 straight_join 힌트 미사용

뭐가 참 많다. 사실 다 JOIN 쓰면 해결될 문제다. 근데 왜 자꾸 Subquery를 쓰고 싶을까?

JOIN으로 가져오는 단점, 생각?

DDD가 적용된 소프트웨어에서 JOIN을 사용하게 되면, 결국 두 가지 이상의 도메인이 결합된 쿼리가 필요로 하게 된다.
두 가지 이상의 도메인이 결합하여 그 자체로 하나의 도메인이 되는 것이 DDD라고 할 수 있을 것 같다. 다만, 기존에 잘못 분리되었거나 너무 크게 분리된 도메인의 경우 이러한 관점에서 보는 것이 굉장히 애매했다.
만약 서브쿼리를 잘 사용할 수 있다면, 각각의 도메인에서 서브쿼리를 받아 사용하면 어떨까? 하고 생각했다.
Join되는 형태를 보면, 자주 조인되는 테이블과 칼럼이 항상 비슷하였는데, 충분히 재사용할 수 도 있을 것이라 생각한다.
다만, 아직까지 사내에서 사용하는 5.6으로는 서브쿼리를 사용하기에는 너무 까다롭다고 느껴진다.

profile
Backend Developer @비바리퍼블리카

0개의 댓글