- 조인이란 결국 집합을 연결고리(조인 조건)를 통해 서로 연결하는 것이기 때문에 연결고리 상태가 조인에 커다란 영향을 미치게 된다.
- 연결고리 상태란 주어진 조인조건의 연산자의 형태와 그들이 가지고 있는 인덱스의 상태를 말한다.
2.2.1. 연결고리 정상
- 조인의 연결조건 양쪽 테이블에 모두 인덱스가 존재하는 경우
- 어느 방향으로 조인을 해도 큰 문제는 없어 보인다.
- 연결고리 정상인 경우 1:M 의 관계에 있다 하더라도 발생되는 연결작업의 논리적인 양은 동일하다.
- 즉, 연결고리 정상 상태에서 1:M 관계나 연결방향은 수행속도를 좌우하는 큰 요소가 아니다.
- 수행속도를 좌우하는것은 조인 조건보다는 필터조건에 따른 열결 방향이다.
- 처리범위를 최대한 줄인 후 연결하느냐 연결한 후에 필요없는 자료를 버리느냐?
{section}
{column:width=50}
조인처리
SELECT a.fld1, ....., a.fldn
, b.col1, ....., b.coln
FROM tab2 b, tab1 a
WHERE a.key1 = b.key2
AND b.col2 LIKE 'AB%'
AND a.fld1 = '10'
ORDER BY a.fld2
;
{column}
{column:width=50}
{column}
{section}
개념 이해를 위해 극단적인 가정을 해보자.
- Tab1의 Fld1 = '10' 을 만족하는 범위 5000 건
- Tab2의 col2 LIKE 'AB%' 를 만족하는 범위 100 건
- Tab1 : Tab2 = 2 : 1 이며 연결고리로 연결했을때 모두 연결 성공한다.
- 외부적으론 동일한 결과를 내지만 연결방향에 따라 내부적인 처리량의 차이가 엄청난것을 알 수 잇다.
- 조인의 양을 먼저 많이 줄여줄 수 있는 테이블을 먼저 엑세스 하면 처리량이 줄어든다.
- RBO 모드에서는 이퀄조건을 우선하여 잘못된 엑세스 경로를 선택할 수 있다.
- CBO 모드에서는 데이터 분포도에 따라 Tab2가 먼저 엑세스 되도록 실행계획을 수립할 것이다.
- 하지만 실제 데이터에 따라 달라지는 것이므로 어느 방향이라 단언 할 수 없다.
- 사용자가 어느방향의 연결이 유리한지를 정확하게 알고 있다면 ORDERED 나 LEADING 힌트로 제어해 주는 것도 좋다.
2.2.2. 한쪽 연결고리 이상
- 연결고리 이상이 있는 테이블을 먼저 엑세스 하거나 인덱스와 무관하게 소트머지나 해시조인을 수행하도록 해야 한다.
- 이런 이유로 RBO 에서는 한쪽에 인덱스가 없으면 무조건 없는쪽을 먼저 처리하도록 실행계획을 수립한다.
- 이를 역이용하여 연결고리를 강제로 끊어(조인조건 가공) 원하는 실행계획을 유도하기도 한다.
조인되는 컬럼이 1:1 로 대응되지 않는 경우
- 원래 테이블의 키는 a,b,c 이지만 다른 테이블에서는 통함된 d라는 하나의 통합된 외뢰키를 만들어 사용.
- 애초에 설계 잘못으로 다음 두가지 유형중 하나를 선택하여 연결방향을 고정시켜야 한다.
- 상황변화에 유연하게 대처하지 못하므로 이런식의 설계는 지양해야 한다.
{section}
{column:width=20}
SELECT *
FROM tab1, tab2
WHERE a||b||c = d
;
{column}
{column:width=20}
SELECT *
FROM tab1, tab2
WHERE a = SUBSTR(d, 1, 2)
AND b = SUBSTR(d, 3, 1)
AND c = SUBSTR(d, 4, 3)
;
{column}
{column:width=60}
{column}
{section}
데이터 타입의 차이에 의해 발생되는 경우
- 연결고리 조인 컬럼의 데이터 타입이 서로 다른 경우
- CHAR 와 NUMBER 연결시 CHAR 가 NUMBER 로 자동 형변환되어 한쪽 연결고리 이상 발생
- CHAR 와 DATE 연결시 CHAR 가 DATE 로 자동 형변환되어 한쪽 연결고리 이상 발생
- 마찬가지로 설계의 문제
2.2.3. 양쪽 연결고리 이상
- 양쪽 모두 연결고리에 인덱스가 없다면 억지 NL조인은 엄청난 횟수의 풀테이블스캔이 발생된다.
- 따라서 연결고리 상태에 영향을 받지 않는 소트머지나 해시조인 방식으로 실행계획을 수립하게 된다.
- 물론 대량의 경우 이방식이 더 유리할수도 있으나 어느 한가지 방식만을 고집할수 없으므로
- 여러 상환에 유연하게 대처가 가능하도록 연결고리 정상상태를 만들어 주는것이 좋겠다.
연결고리 이상 실전 예제 1
{section}
{column:width=50}
BETWEEN
SELECT *
FROM tab1 x, tab2 y
WHERE y.key1 = x.key1
AND y.key2 BETWEEN '200501' AND '200503'
;
{column}
{column:width=50}
{column}
{section}
- y의 인덱스가 key1 + key2 이라면 연결고리 정상
- y의 인덱스가 key2 + key1 이라면 연결고리 이상 : 선두컬럼이 = 조건이 아닌 범위 조건임
- Between 을 In으로 바꾸어 = 조건을 만들어 줌
{section}
{column:width=50}
IN
SELECT *
FROM tab1 x, tab2 y
WHERE y.key1 = x.key1
AND y.key2 IN ('200501', '200502', '200503')
;
{column}
{column:width=50}
{column}
{section}
- 범위조건이 항상 IN 으로 대체 가능한것은 아니다.
- 이경우 In 서브쿼리를 이용할 수 있다.
{section}
{column:width=50}
IN 서브쿼리
SELECT *
FROM tab1 x, tab2 y
WHERE y.key1 = x.key1
AND y.key2 IN (SELECT ymd
FROM date_dual
WHERE ymd BETWEEN :start_Dt AND :end_Dt
)
;
{column}
{column:width=50}
{column}
{section}
- 이론적으로는 연결고리 이상을 만들지 않을 수 있을것 같다.
- 하지만 서브쿼리가 나타나는 순간 문제는 간단하지가 않다.
- tab2가 key2의 조건값을 상수값으로 받기 위해선 서브쿼리가 먼저 수행되어야 하는데
- 이러한 제약은 이미 tab1과 tab2 사이의 평등관계에 영향을 미치기 때문이다.
연결고리 이상 실전 예제 2
- Tab1 > (Tab2 , Tab3) 로 연결된다면 연결고리 정상
- Tab2 > Tab1 > Tab3 또는 Tab3 > Tab1 > Tab2 로 연결된다면 연결고리 이상
- (Tab2 , Tab3) > tab1 로 연결될수 있다면 연결고리 정상 가능하다.
- Tab2 와 Tab3 의 자료가 극히 적다고 가정하고 카티젼곱을 통해 이를 구현한 것이 스타조인이다.
연결고리 이상 실전 예제 3
- key1 은 tab1에서 상속받아 tab2, tab3 까지 이어졌다.
- 이때 (b)와 같이 x와 y를 비교하게 된다면?
- z.key1이 상수값으로 주어진다고 해도 x에서는 이를 사용못하며, 반드시 y를 거쳐야만 x를 읽을 수 있다.
- z.key 에 상수 조건이 주어진다면 쿼리변환에 의해 x나 y도 바로 상수조건이 적용되지만
- z.key 에 상수 조건이 주어진것이 아니므로 실행계획이 한쪽 방향으로만 고정된다.(z > y > x)
- 이렇듯 조인절을 어떤 테이블끼리 비교하느냐에 따라 연결고리 이상이 발생할 수 있으며
- 조건절의 테이블 알리아스만 살짝 변경해주는것만으로도 엄청난 성능향상 효과가 발생되기도 한다.