SQL 가상 테이블 : VIEW vs CTE vs Subquery, 최종 설계 및 선택 가이드

데이터베이스를 설계할때 한 번쯤은 마주하는 결정의 순간이 있습니다. “이 로직을 서브쿼리로 넣을까, WITH 절로 뺄까, 아니면 VIEW로 정의할까?” 단순히 쿼리가 돌아가게 만드는 단계를 넘어, 성능, 유지보수, 운영 관점에서 어떤 선택이 최선인지 분석하고, 수백만 건의 데이터 환경에서도 견고하게 버티는 최종 설계 가이드라인을 생각해보겠습니다.


1. 가상 테이블의 본질과 옵티마이저

우리가 사용하는 VIEW, CTE, Subquery는 모두 “테이블처럼 보이지만 물리적 데이터는 존재하지 않는 논리적 구조”라는 공통점이 있습니다. 하지만 데이터베이스 엔진(옵티마이저)이 이들을 처리하는 방식은 판이합니다.

① VIEW: 전사적 인터페이스

VIEW는 DB 카탈로그에 쿼리 정의가 저장된 영구 객체입니다.

  • 동작 원리: 사용자가 VIEW를 조회하면 옵티마이저는 VIEW의 정의를 본 쿼리와 합치는 'Query Transformation'을 수행합니다.
  • 장점: 복잡한 비즈니스 로직을 캡슐화하고 권한 제어를 가능하게 합니다.
  • 단점: 중첩된 VIEW(Nested View)는 실행 계획을 복잡하게 만들어 성능 예측을 불가능하게 합니다.

② CTE (WITH 절): 절차적 가독성

CTE는 단일 쿼리 실행 중에만 존재하는 임시 명칭입니다.

  • 동작 원리: MySQL 8.0 이상에서는 필요에 따라 결과를 메모리에 담는 '구체화(Materialization)' 전략을 사용합니다.
  • 장점: 쿼리 구조를 위에서 아래로 흐르게 만들어 가독성을 극대화하며, 재귀 탐색(Recursive)이 가능한 유일한 도구입니다.
  • 단점: 임시 테이블 생성 시 인덱스가 소실되어 대량 데이터 조인 시 성능 저하가 발생할 수 있습니다.

③ 서브쿼리: 인라인 연산 

가장 원초적이고 직관적인 방식입니다.

  • 동작 원리: 쿼리 내부에 직접 삽입되어 실행됩니다.
  • 장점: 별도의 정의 없이 즉각적으로 필터링이나 계산이 필요할 때 편리합니다.
  • 단점: 가독성을 해치고, 특히 상관 서브쿼리의 경우 루프처럼 반복 실행되어 성능을 저하시킵니다.
반응형

2. 성능 최적화의 핵심: 트레이드 오프(Trade-off) 

기술 선택의 핵심은 무엇을 얻고 무엇을 포기할 것인가를 결정하는 것입니다.

성능 vs 가독성

  • 서브쿼리는 최적화가 용이할 때도 있지만 코드가 복잡해지면 디버깅이 불가능합니다.
  • CTE는 가독성이 압도적이지만, DB 엔진이 이를 물리적인 임시 테이블로 만드는 순간 인덱스 활용 능력을 잃어버립니다.
  • 결론: 데이터가 적다면 CTE를, 데이터가 방대하고 인덱스 활용이 필수적이라면 뷰 병합(View Merge)이 가능한 단순 구조를 선택해야 합니다.

보안 vs 유연성

  • VIEW는 특정 컬럼만 노출하는 강력한 보안 레이어를 제공합니다. 하지만 한 번 정의되면 수정 시 영향도 파악이 어렵습니다.
  • CTE와 서브쿼리는 유연하지만 호출하는 사용자가 원본 테이블에 대한 권한을 직접 가져야 하므로 보안상 취약할 수 있습니다.

3. 실무 상황별 최종 선택 가이드

아래는 개인적인 상황별 쿼리 검토 가이드 입니다.

1. 무조건 JOIN부터 검토!

성능 면에서는 서브쿼리보다 JOIN이 거의 항상 우위에 있습니다. IN이나 EXISTS 대신 적절한 인덱스가 걸린 JOIN을 활용하는 것이 튜닝의 시작입니다.

 2. 쿼리가 많은 줄을 넘어간다면 CTE를 써라

복잡한 리포트용 쿼리나 단계별 집계가 필요한 경우, 서브쿼리를 중첩하지 마십시오. CTE를 통해 준비물과 메인 로직을 분리하는 것만으로도 유지보수 비용을 대폭 줄일 수 있습니다.

3. VIEW는 "정책"과 "보안"의 도구로 한정하라

  • 정책: 각각의 화면에서 활성유저의 정보를 조인하거나 가져다 사용중인데 활성 유저의 기준이 최근 3개월 접속에서 최근 1개월 접속으로 바뀐다면? 여러개의 쿼리를 고치는 대신 하나의 VIEW만 수정하면 됩니다.
  • 보안: 민감한 개인정보를 제외한 통계용 VIEW를 제공하여 데이터 유출 사고를 방지하십시오.

4. 절대로 피해야 할 '안티 패턴' 

  1. VIEW 안에 또 VIEW (Deep Nesting): 3단계 이상의 VIEW 중첩옵티마이저를 무력화시킵니다. 인덱스가 있어도 풀 스캔을 하게 되는 주범입니다.
  2. CTE 내의 대량 데이터 가공: CTE 결과값이 수백만 건인 상태에서 본 쿼리와 조인하면 안됩니다. 임시 테이블 풀 스캔으로 인해 서버가 멈출 수 있습니다.
  3. WHERE 절의 상관 서브쿼리: 메인 테이블의 모든 행에 대해 서브쿼리가 실행됩니다. 10만 건의 데이터라면 10만 번의 쿼리가 날아가는 것과 같습니다.

5. 마무리

기술은 도구일 뿐, 목적이 되어서는 안되는것 같습니다. 제가 항상 느끼는 점은 "좋은 쿼리는 컴퓨터가 빠르게 읽고, 사람이 쉽게 이해하는 쿼리"라는 것입니다.

  • 성능이 문제라면 JOIN과 인덱스를 먼저 살피자.
  • 가독성이 문제라면 WITH (CTE)로 로직을 쪼개자.
  • 공통 정책이 문제라면 VIEW를 사용하자.

이 세개의 구문을 사용해서 쿼리의 성능을 향상시키고 적재적소에 배치하여 사용하기를 바라겠습니다.

https://gapal.tistory.com/98

https://gapal.tistory.com/97

https://gapal.tistory.com/96

반응형