#title SQL 실행결과의 해석 [[TableOfContents]] ==== 문제 ==== 특정 쿼리를 작성하였다. 2가지 방식으로 작성하였는데 다음과 같은 결과를 보였다. 실행계획의 예상하고, 결과를 분석해보라. '''조건: ''' * carName 테이블 : 200 건, * totalData 테이블 : 13만건 '''1. 실행계획''' * case 1 : 60% (일반조인 inner join) * case 2 : 40% (서브쿼리를 이용하여 데이터만 집계한 후 설명테이블과 조인) 실행계획 상으로는 2번째가 자원을 덜 소모하는 것으로 나타났다. ' '''2. statistics io 결과''' case 1: Table 'totalData'. Scan count 1, logical reads 2990, physical reads 0, read-ahead reads 0. Table 'carName'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0. case 2: Table 'carName'. Scan count 140, logical reads 280, physical reads 0, read-ahead reads 0. Table 'totalData'. Scan count 1, logical reads 2990, physical reads 0, read-ahead reads 0. ==== Case1 분석 ==== 실행계획상에 나타난 자원의 사용량으로 보아서는 Case2가 더 유리한 것으로 나타났다. 그러나 Case2를 보면 더 많은 데이터를 읽은 것을 볼 수 있다. Case1의 Statistics io를 보면 Merge, Loop, Hash 중 어떤 조인으로 실행계획이 풀렸는지 알 수는 없다. 그렇지만 두 테이블의 데이터 양이 차이가 많이 나므로 Merge Join의 가능성은 매우 적다. 그러므로 Hash Join 또는 Loop Join이 되었을 것이다. 이 두 가지 가능성에 대해서 알아보자. '''Hash Join의 가능성''' Statistics io의 결과를 보면 알 수 있듯이 두 테이블은 Scan Count가 1이다. 이것이 의미하는 것은 carName 테이블을 접근할 집합이 1건이거나 200건을 풀스캔하거나 인덱스 스캔을 하였다 하더라도 2개의 페이지 속에 데이터가 모두 들어갔을 수 있다는 것이다. 만약 200건을 모두 풀스캔하였거나 인덱스 스캔을 하였다면 Hash Join의 가능성이 크다. '''Loop Join의 가능성''' 만약 Join되는 carName의 집합이 1건이라면 Loop Join의 가능성이 매우 크다. 또한 Join이 되는 컬럼에 양쪽 다 인덱스가 있었다면 Loop Join의 가능성이 매우 크다. 만약 인덱스가 생성되지 않았다면 이는 Hash Join의 가능성이 매우 크다. Statistics io를 볼 때 만약 Loop Join으로 풀렸다면 양쪽에 인덱스가 있는 것으로 데이터 양은 2페이지에 들어갈 정도로 매우 작다. 만약 carName을 먼저 읽었고, Loop Join이라면 totalData 테이블의 연결 고리가 되는 컬럼에는 Clustered Index가 생성되었을 것이다. 즉, carName을 1건 읽고, totalData의 Clustered Index를 쭉 읽었을 가능성이다. 시나리오는 이렇지만 필자의 느낌으로는 매우 Loop Join은 매누 희박하다. 더 많은 것을 분석하기 위해서는 Case2의 분석이 필요하다. '''Merge Join의 가능성''' Merge Join의 가능성도 Hash Join과 같이 매우 높다. 하지만 200건과 13만 건의 차이가 있으므로 Hash Join의 가능성이 높다. 연결고리로 정렬까지 필요했다면 Merge Join을 했을 것이다. ==== Case2 분석 ==== Case2는 집계 후 Join을 했다고 했다. Case2의 Statistics io의 양으로 보아서는 totalData의 양은 전체 테이블 대상인지 일부분인지는 판단될 수 없다. 테이블을 좁게 설계하였다면 논리적 읽기 수로 보아서 전체를 대상으로 하였을 것이다. Case2의 statistics io를 보면 carName의 Scan Count가 140인 것을 알 수 있다. 그러므로 위에서 본 Case1의 carName 테이블의 statistics io가 2인 것을 감안한다면, 140 * 2 = 280 인 것이 논리적 읽기 수가 맞아 떨어진다. 이는 많은 것을 의미한다. 결과 집합이 140건임을 의미하고, totalName을 특정한 그룹으로 나누니 140가지의 종류라는 것이다. 또한 140회의 Join을 했으며, Join방식은 Loop Join방식이었다. 실제로 선택도는 좋지 않을 것으로 판단된다. ==== 결론 ==== 그럼 어떤 것이 유리할까? 특별한 답은 없다. 그러나 같은 결과를 얻기 위해서는 집계를 해야 한다는 것을 결과적으로 같을 것이다. 그냥 Join해서 결국은 Select 절에서 집계를 했을 것이다. 그러므로 이 쿼리는 전체처리를 해야지만 원하는 결과를 얻을 수 있다. Case2의 경우는 같은 결과를 얻기 위해서 totalName을 Distinct 하게 만들어 Join 시키기 전 Join되는 양을 줄여 Join의 부하를 줄였다. 또한 Loop Join이므로 totalName이 집계되는 시간 동안에만 기다리면 되는 특정 부분만 전체 처리를 하고, Join시는 부분 범위 처리를 한 것이다. 실제로 Case2가 더 빠른 응답시간을 가진 가장 큰 요소는 Join 횟수의 축소였다. 그러므로 이를 Hash Join으로 풀었다면 더 좋은 처리 효과를 얻을 수 있었을 것이다. 실제 Join되는 양도 적기 때문에 전체 범위로 처리하는 것이 더 유리할 수 있었다고 할 수 있다. 그래서 위 문제의 정보만으로는 확신할 수 없다. 필자가 확정을 짓지 못하는 것은 이런 상황 외에도 무수히 많은 상황이 존재할 수 있기 때문이다. CPU가 병목인데 Hash Join을 유도한다면 더 느린 응답시간을 보일 수도 있고, 해당 Grid 컴포넌트가 부분 범위처리를 지원하지 않는다면 전체 범위처리로 더 유리한 방법을 찾아 보아야 한다.