SQL의 기본의 select 다.
정형이건 비정형이건 목적 지향적 언어이므로 가장 중요한 것은 select 이며 모델러의 역량은 최종 결과물인 select를 기준으로 한다.
모델러의 머리속에 그려지는 dataset의 다양한 조건에 맞게 설계하게 되는데 이때 select 쿼리 작성 능력에 따라 설계하기 마련이다.
특정 테이블에 생각외로 컬럼이 많고 빈 컬럼이 많다면 모델러의 쿼리작성 능력을 의심해 볼만하다.
반대로 모델링된 테이블들을 업무를 배제한채 함부로 판단해서도 안된다.
그만큼 상황에 따른 대처방법이 다양한게 모델링이기 때문이다.
select는 거창하게 말하지만 사실상 별게 없다.
출력되는 부분은 select절에 대상 테이블은 from절에 필터링 조건은 where 절에 사실상 이게 전부이다.
엑셀의 필터기능과 크게 다를 바가 없다.
(일단 이번글에서 index 는 index 카테고리에서 참고하고 다루지 않을것이며 초초초 기초적인 selelct 문 정도는 아는것을 기초로 설명하겠다.)
우선 간단하게 join의 개념을 살짝 알아보자.
조인의 종류는 크게 inner와 outer cross 가 있고
조인의 방법에는 NL(Nested Loop),SM(Sort Merge : MS-SQL에서는 Merge join이라 부른다.),HASH가 있다.
이정도 join의 개념을 개발 2년차가 되면 다들 알고 있을 거라 생각할 수 있다.
하지만 실전에서 과연 정확히 알고 있을까?
10년차 이상의 고급언어 개발자라면 어떨까?
예상데로(?) 대부분 대충.. 심지어 전혀 모르고 사용한다.
좀 심하게 말하자면 대부분 90% 이상의 고급언어 개발자들이 SQL 작성에 관련하여 아가리파이터가 대부분이다.
여튼 join에 대해서는 이후 장에서 상세히 다루기로 하고
선행(Driving, Build)과 후행(Drived, Prove)을 알아야 한다.
이 개념이 없다면 NL과 Hash를 이해할 수 없을 뿐더러 모든 select 조인의 기본이기도 하기 때문이다.
4개의 테이블을 조인한다고 가정하자
이때 기준이 되는 선행 테이블의 선정과 조인의 순서가 무척 조용하다.
A테이블은 10만건을 소유하며 조회 조건에 의해 필터링 되면 100건이 대상이다.
B테이블은 100만건을 소유하며 조회 조건에 의해 필터링 되는 경우 10건이 대상이 되며 A테이블과 조인시에는 100건으로 다시 증가한다.
C테이블은 Outer join 대상으로 A테이블과 조인되나 where 절로 필터링시 예상 출력 10건이다.
D테이블은 Outer join 대상으로 B테이블과 조인되나 where 절로 필터링시 예상 출력 5건이다.
보통의 경우 A-B-C-D로 쿼리문을 배치하는 경우가 9할이다.
아래 이미지에서 조인될시 row count를 곱해 보자.
파란색의 경우 10 x 10 x 10 x 5 = 5,000 이며
붉은색의 경우 100 x 100 x 10 x 5 = 500,000 이다.
인덱스 배치나 page 분할에 따라 다르고 결과는 동일하나 분명 파란색이 빠를 것이다.
속도만이 문제가 아니라 다중 사용자가 일반적인 경우에 래치경합도 문제가 될 수 있다.
여기서 우리가 알아야 할 상식이 있다.
옵티마이저는 생각보다 멍청하다는 것이다.
멍청하기 보다 보수적이다.
그래서 습관이 중요하다.
여기서는 파란색(B-C-A-D) 순서로 조인문을 구성하는게 좋다.
중등 교육과정의 집합을 생각해 보자.
집합과 집합들 사이의 교집합, 합집합, 여집합, 부분집합,,
큐브로 떠올려도 좋고 매트릭스로 그려보아도 좋다.
습관적으로 고급언어(자바, .NET등) 코딩하듯 조건문과 순환문으로 사고하고 SQL을 작성하면 아무것도 그려지지 않을 것이고 뇌의 가소성 으로 미루어 집합연산 개념은 시간 지속될수 록 더욱 상상할 수 없게 된다.
우선 대부분의 유료 DBMS의 경우 조인 순서를 사용자가 생성한 테이블 순서대로 고정시키는 옵션이 있다.
오라클의 경우 ordered, Mysql STRAIGHT_JOIN, MS-SQL의 경우 option(force order) 로 표현된다.
오라클이라면 leading 힌트로 사용자의 쿼리문 순서와 무관하게 임의로 순서를 설정할 수 도 있다.
(아쉽지만 MS-SQL에서는 leading 이 지원하지 않는다. Postgresql의 경우 유료 EPAS 에서 지원한다.)
기본적으로 특별히 우위가 없이 동등하다면 작성된 쿼리문내 테이블 순서를 따르겠으나 옵티마이저의 신뢰 지수는 복잡한 구조일 수록 신뢰성이 높지 않으며 엉뚱한 조인순서로 조인할 수 있다.
결국 습관적으로 조인 순서에 맞게 테이블을 조인하는게 맞다.
이러한 조인의 순서는 습관적으로 머리속에 고정시켜도 좋다.
우선 Driving table을 선정하고 이후에는 결과값을 최소화 할 수 있는 집합끼리 우선 매핑하는 것이다.
Driving table의 선정역시 결과값을 최소(정확히는 비용 최소하 인덱스의 활용과 물리면 조금은 복잡하긴 하다.) 하는 방식이다.
대용량 데이터 조인의 경우(ETL이 대표적이다.) 많은 경우 hash join으로 진행된다.
이때 가장 중요한 부분이 조인의 순서이다.
늘 말하지만 옵티마이저는 절대 신뢰의 대상이 아니다.
데이터베이스의 SQL 작성시 시야를 Procedural 에서 Set으로 전환하지 않는다면..
여전히 복잡한 쿼리를 보면 지루박으로 머리속에 연상이 된다면 중학교 수학 집합 부분을 상상해 보자.
오랜기간 SQL 튜닝을 사이트별로 다양한 DBMS별로 다양한 환경에서 보았지만 가장 기본기 문제로 인한 쿼리문 변경이 대다수였다.
물론 프로젝트 기간에 따른 이슈가 주요 외부적 요인으로 보일 수 있겠으나 내 경험으로는 그냥 기초 부족이 더 문제다.
근래 CTE와 inline- view의 사용에 따른 이견이 많다.
상황에 따라 다르겠지만 가독성과 조인의 순서를 기준으로 탄력적으로 구성하면 아무 문제 없어 보인다.
힌트에 보수적인 사이트에서 특히나 매우 유용하다.
누구나 예외 없이 연약하고 무력하게 태어난다. - 누수바움 "타인에 대한 연민"
이 글은 이비니어스( www.evinious.co.kr )의 소유이며 불펌은 허락하지 않습니다.
'데이터베이스 SELECT' 카테고리의 다른 글
Set vs Procedure (3) | 2025.02.04 |
---|