* 조인 순서와 유형 선택
1. 중첩 루프 조인(nested loop join) - 외부 입력 테이블(outer table)이 작고, 내부 입력 테이블(inner table)에 인덱스가 있는 경우에 잘 작동한다.
2. 병합 조인(merge join) - 입력 테이블이 둘 다 조인 컬럼을 기준으로 정렬되어 있는 경우에 잘 작동한다.(필요하다면, 옵티마이저가
테이블들중 하나를 정렬할 수 있다.
3. 해시 조인(hash join) - 사용할 수 있는 인덱스가 없는 경우에 잘 수행된다. 대개, 인덱스를 만들어 주면 다른 조인 전략을 선택하게 되어 성능이
향상될 수 있다.
OPTION (FORCE ORDER)절을 사용하고 SET FORCEPLAN ON 세션 옵션을 사용하면 옵티마이저가 조인 순서를 결정하는 기능을 무시할 수 있다.
OPTION (FORCE ORDER)절을 사용하고 SET FORCEPLAN ON 세션 옵션은 옵티마이저가 FROM절에서 지정된 순서대로 테이블을 조인하도록 한다.
- 중첩 루프 조인
루프 내에 루프가 있는 형태이다. 중접 루프 조인은 하나의 테이블을 외부 입력 테이블로 지정하고, 다른 테이블을 내부 입력 테이블로 지정한다.
외부 루프를 반복할 때마다 전체 내부 루프가 수행된다. 크기가 작거나 중간 크기인 테이블에 좋지만, 루프가 커질수록 이 전략은 차츰 비효율이 된다.
- 병합 조인
데이터 집합의 크기가 큰 경우에는 병합 조인(merge join)이 중첩 루프 조인보다 훨씬 더 효율적으로 수행된다.
병합 조인을 수행하려면 양쪽 테이블이 모두 병합 컬럼에 대해 정렬되어 있어야 한다. 옵티마이저는 대개, 조인된 컬럼에 대해 이미 정렬되어 있는
크기가 큰 데이터 집합으로 작업할 때 병합 조인을 선택한다.
옵티마이저는 인덱스 트리에 사용하여 정렬된 입력을 제공할 수 있으며, GROUP BY, CUBE, ORDER BY의 정렬 연산에도 활용할 수 있다.
옵티마이저는 CROSS JOIN과 FULL JOIN을 제외한 모든 형태의 관계형 조인에 대하여 병합 조인 연산을 수행할 수 있다. 병합 조인은 테이블들을
UNION으로 조인하는 데도 사용될 수 있다.(UNION을 사용하면 중복 값을 제거하기 위하여 테이블을 정렬해야 하기 때문이다.)
- 해시 조인
해시 조인도 크기가 큰 데이터 집합의 경우에 중첩 루프 조인보다 훨씬 더 효율적이다. 옵티마이저는 크기가 큰 입력 데이터를 처리하고 테이블들을
조인할 인덱스가 없거나, 인덱스가 있기는 하지만 사용할 수 없을 때에는 대개 해시 조인을 선택한다.
해시 조인은 두 테이블 중에서 더 작은 테이블의 행들을 해싱하여 해시 테이블로 저장한 다음에, 더 큰 테이블의 행을 한 번에 하나씩 처리하고
해시 테이블을 스캔하여 일치하는 데이터를 찾는다. 두 테이블 중 더 작은 테이블이 해시 테이블에 있는 값을 제공하기 때문에, 해시 테이블
크기가 최소로 유지되며, 실제 값 대신 해시된 값을 사용하기 때문에 테이블 간의 비교가 신속하게 이루어질 수 있다.
* 논리 연산자와 물리 연산자
물리 연산자는 쿼리를 실행하기 위해 SQL 서버가 어떤 작업을 하는지를 표시한다.
논리 연산자는 SQL문을 처리하는 데 사용되는 관계적인 연산을 표시한다.
-DISTINCT
옵티마이저가 쿼리에서 DISTINCT나 UNION을 만나면, 결과 집합을 반환하기 전에 입력들로부터 중복 데이터를 제거해야 한다.
두 가지 선택을 할 수 있다. 중복 데이터를 제거하기 위해 데이터를 정렬할 수 있거나 또는 해시를 사용할 수 있다.
옵티마이저는 해시나 정렬 물리 연산자를 사용하여 DISTINCT나 UNION 논리 연산을 처리할 수 있다.
-GROUP BY
옵티마이저는 정렬을 사용하거나 해싱을 사용하여 GROUP BY를 처리할 수 있다.
물리 연산자는 HASH나 SORT가 될 수 있지만, 논리 연산자는 AGGREGATE OR 이다.
또한, GROUP BY의 경우에는 STREAM AGGREGATE를 많이 선택한다.
옵티마이저가 데이터를 그룹들로 만들기 위해서 HASH 연산을 수행하는 것을 선택할 수 있기 때문에, 결과 집합이 정렬된 순서로
나오지 않을 수 있다. GROUP BY를 사용했다고 해서 자동으로 데이터가 정렬될 것이라고 기대해서는 안 된다.
정렬된 결과 집합을 원하면, ORDER BY 절을 기술해야 한다.
-ORDER BY
ORDER BY의 경우에도 옵티마이저가 내려야 하는 결정이 있다. 이미 정렬되어 있는 데이터에 클러스터된 인덱스가 없다고 가정하면,
옵티마이저는 요청된 순서대로 결과 집합을 반환하는 방법을 생각해 내야 한다. 물론 우리가 기대하는 대로 옵티마이저가 데이터를
정렬할 수도 있고 아니면 적절한 키를 가지는 클러스터되지 않은 인덱스의 잎 레벨을 조회할 수도 있다. 옵티마이저가 이를 처리하는
방법은 많은 요소들에 의해 좌우된다. 가장 큰 요소가 선택성(selectivity)이다.
쿼리를 실행하면 얼마나 많은 행들이 반환될 것인가? 동등하게 관련 있는 요소가 인덱스 커버링(covering)이다.
클러스터되지 않은 인덱스가 쿼리를 커버할 수 있는지의 여부이다. 행의 개수가 비교적 적으면, 전체 테이블을 정렬하는 것보다
클러스터되지 않은 인덱스를 사용하는 것이 비용이 더 저렴할 수 있다. 마찬가지로, 인덱스가 쿼리를 커버할 수 있다면, 클러스터된
인덱스를 가지는 것 다음으로 좋은 인덱스를 가지게 되는 것이며, 옵티마이저는 그 커버링된 인덱스를 사용할 가능성이 높다.
-스풀링
쿼리 계획에 있는 스풀링(spooling) 연산자는 나중에 처리할 수 있도록 옵티마이저가 중간 쿼리의 결과를 테이블에 저장하는 것을
나타낸다.
지연된 스풀(lazy spool)의 경우에는 필요할 때 작업 테이블이 채워진다. 신속한 스풀(eager spool)의 경우에는 테이블이 하나의
단계에서 저장된다. 옵티마이저는 신속한 스풀에 비해 지연된 스풀을 선호한다. 지연된 스풀을 사용하면 쿼리 계획에 있는
로직에 기반하여 완전하게 작업 테이블을 채우지 않고도 작업을 수행할 수 있기 때문이다.
신속한 스풀이 필요한 경우가 종종 있다. 예를 들어, 할로윈 문제(Halloween problem)로부터 보호하기 위해서 필요하다.
그러나 지연된 스풀 방식의 오버헤드가 적기 때문에 옵티마이저는 대개 지연된 스풀을 선호한다.
(참고 : 할로윈 문제란? 간단하게 말해서 같은 행에 대하여 여러 번 업데이트가 수행되는 문제를 말한다. 업데이트된 행이 UPDATE
작업이 수행되는 중에 업데이트될 행들이 있는 곳으로 이동함으로써, 같은 행에 대하여 여러 번 업데이트되는 문제가 발생한다.
초기 DBMS 시절에는 벤더들이 일단의 데이터를 수정할 때 대개 한 번에 하나의 행을 업데이트했기 때문에 이런 문제가 흔히
발생했다. 만약 그 업데이트가 행을 정렬하는 키 컬럼을 변경한다면, 변경된 행은 그 행들의 그룹 내의 어딘가로 이동할 것이다.
그 때 그룹에서 위치적으로 뒷부분으로 이동한다면 그 값은 나중에 한 번 더 변경될 것이다.)
인덱스뿐 아니라 테이블에 대해서도 스풀 연산자를 수행할 수 있다. 옵티마이저가 행이 있는지를 알기만 하면 되는 경우에는
rowcount 스풀을 사용한다.
-옵티마이저의 물리 연산자
ASSERT - 하나의 행만 반환할 수 있는 상황에서 서브쿼리가 사용되고 있다는 것을 의미한다. Assert를 제거하려면 서브쿼리에
SELECT TOP 1 을 사용하면 된다.
BOOKMARK LOOKUP - 테이블이나 클러스터된 인덱스에 있는 데이터를 찾기 위하여 클러스터되지 않은 인덱스에 저장되어 있는
RID 나 클러스터된 인덱스 키 값이 사용된다는 것을 의미한다.
CONSTANT SCAN - 조건이 절대로 TRUE가 될 수 없다는 것을 옵티마이저가 이미 알고 있다는 것을 의미한다.
CURSOR - 서버 커서를 사용하여 작업한다는 것을 의미한다.
OPERATIONS FILTER - 데이터를 전달하기 전에 데이터를 추려내고 있다는 것을 의미한다.(아마도 WHERE문을 사용하여)
JOINS - 중첩 루트 조인, 병합 조인, 해시 조인이 수행되고 있다는 것을 의미한다.
SCAN - 인덱스의 잎 노드를 순차적으로 검색한다는 것을 의미한다.
SEEK - 인덱스 B-트리의 이진(비순차적) 검색을 의미한다.
SORT - 다음 step 으로 데이터를 전달하기 전에 데이터를 정렬한다는 것을 의미한다.
SPOOL - 옵티마이저가 중간 쿼리 계획 단계에서 얻은 결과를 추가적인 처리를 위하여 작업 테이블에 저장하고 있다는 것을 의미한다.
STREAM AGGREGATE - 벡터 집계 또는 그룹핑이 수행되고 있다는 것을 의미한다.
1. 중첩 루프 조인(nested loop join) - 외부 입력 테이블(outer table)이 작고, 내부 입력 테이블(inner table)에 인덱스가 있는 경우에 잘 작동한다.
2. 병합 조인(merge join) - 입력 테이블이 둘 다 조인 컬럼을 기준으로 정렬되어 있는 경우에 잘 작동한다.(필요하다면, 옵티마이저가
테이블들중 하나를 정렬할 수 있다.
3. 해시 조인(hash join) - 사용할 수 있는 인덱스가 없는 경우에 잘 수행된다. 대개, 인덱스를 만들어 주면 다른 조인 전략을 선택하게 되어 성능이
향상될 수 있다.
OPTION (FORCE ORDER)절을 사용하고 SET FORCEPLAN ON 세션 옵션을 사용하면 옵티마이저가 조인 순서를 결정하는 기능을 무시할 수 있다.
OPTION (FORCE ORDER)절을 사용하고 SET FORCEPLAN ON 세션 옵션은 옵티마이저가 FROM절에서 지정된 순서대로 테이블을 조인하도록 한다.
- 중첩 루프 조인
루프 내에 루프가 있는 형태이다. 중접 루프 조인은 하나의 테이블을 외부 입력 테이블로 지정하고, 다른 테이블을 내부 입력 테이블로 지정한다.
외부 루프를 반복할 때마다 전체 내부 루프가 수행된다. 크기가 작거나 중간 크기인 테이블에 좋지만, 루프가 커질수록 이 전략은 차츰 비효율이 된다.
- 병합 조인
데이터 집합의 크기가 큰 경우에는 병합 조인(merge join)이 중첩 루프 조인보다 훨씬 더 효율적으로 수행된다.
병합 조인을 수행하려면 양쪽 테이블이 모두 병합 컬럼에 대해 정렬되어 있어야 한다. 옵티마이저는 대개, 조인된 컬럼에 대해 이미 정렬되어 있는
크기가 큰 데이터 집합으로 작업할 때 병합 조인을 선택한다.
옵티마이저는 인덱스 트리에 사용하여 정렬된 입력을 제공할 수 있으며, GROUP BY, CUBE, ORDER BY의 정렬 연산에도 활용할 수 있다.
옵티마이저는 CROSS JOIN과 FULL JOIN을 제외한 모든 형태의 관계형 조인에 대하여 병합 조인 연산을 수행할 수 있다. 병합 조인은 테이블들을
UNION으로 조인하는 데도 사용될 수 있다.(UNION을 사용하면 중복 값을 제거하기 위하여 테이블을 정렬해야 하기 때문이다.)
- 해시 조인
해시 조인도 크기가 큰 데이터 집합의 경우에 중첩 루프 조인보다 훨씬 더 효율적이다. 옵티마이저는 크기가 큰 입력 데이터를 처리하고 테이블들을
조인할 인덱스가 없거나, 인덱스가 있기는 하지만 사용할 수 없을 때에는 대개 해시 조인을 선택한다.
해시 조인은 두 테이블 중에서 더 작은 테이블의 행들을 해싱하여 해시 테이블로 저장한 다음에, 더 큰 테이블의 행을 한 번에 하나씩 처리하고
해시 테이블을 스캔하여 일치하는 데이터를 찾는다. 두 테이블 중 더 작은 테이블이 해시 테이블에 있는 값을 제공하기 때문에, 해시 테이블
크기가 최소로 유지되며, 실제 값 대신 해시된 값을 사용하기 때문에 테이블 간의 비교가 신속하게 이루어질 수 있다.
* 논리 연산자와 물리 연산자
물리 연산자는 쿼리를 실행하기 위해 SQL 서버가 어떤 작업을 하는지를 표시한다.
논리 연산자는 SQL문을 처리하는 데 사용되는 관계적인 연산을 표시한다.
-DISTINCT
옵티마이저가 쿼리에서 DISTINCT나 UNION을 만나면, 결과 집합을 반환하기 전에 입력들로부터 중복 데이터를 제거해야 한다.
두 가지 선택을 할 수 있다. 중복 데이터를 제거하기 위해 데이터를 정렬할 수 있거나 또는 해시를 사용할 수 있다.
옵티마이저는 해시나 정렬 물리 연산자를 사용하여 DISTINCT나 UNION 논리 연산을 처리할 수 있다.
-GROUP BY
옵티마이저는 정렬을 사용하거나 해싱을 사용하여 GROUP BY를 처리할 수 있다.
물리 연산자는 HASH나 SORT가 될 수 있지만, 논리 연산자는 AGGREGATE OR 이다.
또한, GROUP BY의 경우에는 STREAM AGGREGATE를 많이 선택한다.
옵티마이저가 데이터를 그룹들로 만들기 위해서 HASH 연산을 수행하는 것을 선택할 수 있기 때문에, 결과 집합이 정렬된 순서로
나오지 않을 수 있다. GROUP BY를 사용했다고 해서 자동으로 데이터가 정렬될 것이라고 기대해서는 안 된다.
정렬된 결과 집합을 원하면, ORDER BY 절을 기술해야 한다.
-ORDER BY
ORDER BY의 경우에도 옵티마이저가 내려야 하는 결정이 있다. 이미 정렬되어 있는 데이터에 클러스터된 인덱스가 없다고 가정하면,
옵티마이저는 요청된 순서대로 결과 집합을 반환하는 방법을 생각해 내야 한다. 물론 우리가 기대하는 대로 옵티마이저가 데이터를
정렬할 수도 있고 아니면 적절한 키를 가지는 클러스터되지 않은 인덱스의 잎 레벨을 조회할 수도 있다. 옵티마이저가 이를 처리하는
방법은 많은 요소들에 의해 좌우된다. 가장 큰 요소가 선택성(selectivity)이다.
쿼리를 실행하면 얼마나 많은 행들이 반환될 것인가? 동등하게 관련 있는 요소가 인덱스 커버링(covering)이다.
클러스터되지 않은 인덱스가 쿼리를 커버할 수 있는지의 여부이다. 행의 개수가 비교적 적으면, 전체 테이블을 정렬하는 것보다
클러스터되지 않은 인덱스를 사용하는 것이 비용이 더 저렴할 수 있다. 마찬가지로, 인덱스가 쿼리를 커버할 수 있다면, 클러스터된
인덱스를 가지는 것 다음으로 좋은 인덱스를 가지게 되는 것이며, 옵티마이저는 그 커버링된 인덱스를 사용할 가능성이 높다.
-스풀링
쿼리 계획에 있는 스풀링(spooling) 연산자는 나중에 처리할 수 있도록 옵티마이저가 중간 쿼리의 결과를 테이블에 저장하는 것을
나타낸다.
지연된 스풀(lazy spool)의 경우에는 필요할 때 작업 테이블이 채워진다. 신속한 스풀(eager spool)의 경우에는 테이블이 하나의
단계에서 저장된다. 옵티마이저는 신속한 스풀에 비해 지연된 스풀을 선호한다. 지연된 스풀을 사용하면 쿼리 계획에 있는
로직에 기반하여 완전하게 작업 테이블을 채우지 않고도 작업을 수행할 수 있기 때문이다.
신속한 스풀이 필요한 경우가 종종 있다. 예를 들어, 할로윈 문제(Halloween problem)로부터 보호하기 위해서 필요하다.
그러나 지연된 스풀 방식의 오버헤드가 적기 때문에 옵티마이저는 대개 지연된 스풀을 선호한다.
(참고 : 할로윈 문제란? 간단하게 말해서 같은 행에 대하여 여러 번 업데이트가 수행되는 문제를 말한다. 업데이트된 행이 UPDATE
작업이 수행되는 중에 업데이트될 행들이 있는 곳으로 이동함으로써, 같은 행에 대하여 여러 번 업데이트되는 문제가 발생한다.
초기 DBMS 시절에는 벤더들이 일단의 데이터를 수정할 때 대개 한 번에 하나의 행을 업데이트했기 때문에 이런 문제가 흔히
발생했다. 만약 그 업데이트가 행을 정렬하는 키 컬럼을 변경한다면, 변경된 행은 그 행들의 그룹 내의 어딘가로 이동할 것이다.
그 때 그룹에서 위치적으로 뒷부분으로 이동한다면 그 값은 나중에 한 번 더 변경될 것이다.)
인덱스뿐 아니라 테이블에 대해서도 스풀 연산자를 수행할 수 있다. 옵티마이저가 행이 있는지를 알기만 하면 되는 경우에는
rowcount 스풀을 사용한다.
-옵티마이저의 물리 연산자
ASSERT - 하나의 행만 반환할 수 있는 상황에서 서브쿼리가 사용되고 있다는 것을 의미한다. Assert를 제거하려면 서브쿼리에
SELECT TOP 1 을 사용하면 된다.
BOOKMARK LOOKUP - 테이블이나 클러스터된 인덱스에 있는 데이터를 찾기 위하여 클러스터되지 않은 인덱스에 저장되어 있는
RID 나 클러스터된 인덱스 키 값이 사용된다는 것을 의미한다.
CONSTANT SCAN - 조건이 절대로 TRUE가 될 수 없다는 것을 옵티마이저가 이미 알고 있다는 것을 의미한다.
CURSOR - 서버 커서를 사용하여 작업한다는 것을 의미한다.
OPERATIONS FILTER - 데이터를 전달하기 전에 데이터를 추려내고 있다는 것을 의미한다.(아마도 WHERE문을 사용하여)
JOINS - 중첩 루트 조인, 병합 조인, 해시 조인이 수행되고 있다는 것을 의미한다.
SCAN - 인덱스의 잎 노드를 순차적으로 검색한다는 것을 의미한다.
SEEK - 인덱스 B-트리의 이진(비순차적) 검색을 의미한다.
SORT - 다음 step 으로 데이터를 전달하기 전에 데이터를 정렬한다는 것을 의미한다.
SPOOL - 옵티마이저가 중간 쿼리 계획 단계에서 얻은 결과를 추가적인 처리를 위하여 작업 테이블에 저장하고 있다는 것을 의미한다.
STREAM AGGREGATE - 벡터 집계 또는 그룹핑이 수행되고 있다는 것을 의미한다.
'연구개발 > DBA' 카테고리의 다른 글
semi join, anti join (0) | 2011.08.22 |
---|---|
스트레스 테스트 STRESS.CMD (0) | 2011.08.22 |
통계 (0) | 2011.08.21 |
히스토그램, DBCC SHOW_STATISTICS (0) | 2011.08.21 |
랜덤 시퀀스 생성(쿠폰번호, 보안티켓 등등) (0) | 2011.08.19 |