계산된 값으로 필터링하기
열에 함수를 적용하면 인덱스 사용이 중단되는 이유와 면접관이 이를 확인하는 방법을 알아봅니다.
계산된 값으로 필터링하기은(는) CoddyKit의 무료 SQL Interview Prep 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 SQL Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. SQL Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
이 질문이 수준을 가르는 이유
질문은 무해하게 들립니다: 이 쿼리는 올바른데 왜 느릴까요? 흔히 그 이유는 WHERE 절이 인덱싱된 열을 함수로 감싸고 있기 때문입니다. 그러면 조건식이 인덱스를 사용할 수 없는 상태가 되어, 최적화기는 더 이상 인덱스를 사용할 수 없고 모든 행을 스캔해야 합니다.
이 레슨에서는 인덱스 검색 가능성을 설명하고, 면접관이 기대하는 수정 방법을 보여 주며, 계산된 필터를 실제로 어디에 넣어야 하는지도 다룹니다.
검색 가능 조건을 한 문장으로 정의하면
검색 가능한 조건(검색 인자를 사용할 수 있음)이란 조건식이 인덱스를 사용해 일치하는 행으로 바로 이동할 수 있다는 뜻입니다. 일반적인 원칙은 인덱싱된 열이 비교식의 한쪽에 그대로 나타나야 하며, 함수나 식 안에 묻혀서는 안 된다는 것입니다.
- 검색 가능:
col = 5,col > 100,col LIKE 'abc%' - 검색 불가능:
FUNC(col) = 5,col + 1 > 100
열에 함수를 적용하는 안티 패턴
여기서 목표는 2024년에 생성된 주문을 찾는 것입니다. 열을 YEAR()로 감싸면 엔진은 비교하기 전에 모든 행 하나하나에 대해 연도를 계산해야 하므로, order_date의 인덱스는 쓸모가 없어집니다.
올바른 결과를 반환하기는 하지만 전체 테이블을 스캔합니다. 큰 테이블에서는 이 차이가 밀리초와 수 분의 차이가 됩니다.
-- non-sargable: function on the indexed column
SELECT *
FROM orders
WHERE YEAR(order_date) = 2024;범위 조건으로 다시 작성하기
해결 방법은 order_date를 그대로 두고 조건을 반열린 범위로 표현하는 것입니다. 그러면 order_date의 인덱스가 2024년의 시작 지점으로 바로 이동하고 2025년에서 멈출 수 있습니다.
결과는 같지만 전체 스캔 대신 인덱스 범위 스캔을 사용합니다. 이 범위 조건으로의 수정은 면접에서 인덱스 검색 가능성을 확인하는 가장 빈번한 해결책입니다.
-- sargable: column stays bare
SELECT *
FROM orders
WHERE order_date >= '2024-01-01'
AND order_date < '2025-01-01';열에 산술 연산을 적용하는 경우
같은 문제는 산술 연산에도 숨어 있습니다. WHERE salary + bonus > 100000이나 WHERE price * 0.9 < 50은 모두 열에 대해 계산을 수행하므로 인덱스 사용을 막습니다.
가능한 경우 계산을 상수 쪽으로 옮기십시오. price * 0.9 < 50을 price < 50 / 0.9로 다시 작성하는 식입니다. 리터럴은 한 번만 계산되고 price는 그대로 유지되어 인덱스를 사용할 수 있습니다.
-- before: math on the column (non-sargable)
WHERE price * 0.9 < 50
-- after: math on the constant (sargable)
WHERE price < 50 / 0.9대소문자를 구분하지 않는 검색의 변형
WHERE LOWER(email) = 'a@b.com'은 email에 대한 일반 인덱스에서는 인덱스를 사용할 수 없습니다. 모든 행의 이메일을 먼저 소문자로 변환해야 하기 때문입니다.
운영 환경에서의 해결 방법은 두 가지입니다. 정규화된 소문자 복사본을 저장하고 그 열에 인덱스를 만들거나, LOWER(email) 식 자체를 인덱싱하는 함수 기반 인덱스를 만드는 것입니다. 함수 기반 인덱스 방식을 언급하면 실무 경험이 있음을 보여 줄 수 있습니다.
-- functional index makes the expression sargable
CREATE INDEX idx_email_lower ON users (LOWER(email));
SELECT * FROM users WHERE LOWER(email) = 'a@b.com';계산이 정말 필요한 경우
범위 조건으로 다시 작성할 수 없고 계산된 값에 필터가 실제로 의존하는 경우도 있습니다. 예를 들어 비율을 기준으로 필터링하는 경우입니다. 그래도 SELECT 별칭은 WHERE에서 참조할 수 없습니다. WHERE가 SELECT 목록보다 먼저 평가되기 때문입니다.
따라서 WHERE에 식을 다시 작성하거나, 쿼리를 하위 쿼리 / CTE로 감싼 다음 바깥 쿼리에서 계산된 열을 기준으로 필터링해야 합니다.
SELECT *
FROM (
SELECT *, revenue / NULLIF(visits, 0) AS rev_per_visit
FROM stats
) t
WHERE t.rev_per_visit > 2.5;집계는 WHERE가 아니라 HAVING에 넣습니다
집계인 계산은 WHERE에 아예 넣을 수 없습니다. WHERE는 그룹화가 이루어지기 전에 개별 행을 필터링하기 때문입니다. WHERE SUM(amount) > 1000은 오류입니다.
집계 필터는 GROUP BY 다음에 실행되는 HAVING에 넣어야 합니다. 어떤 절에서 계산을 볼 수 있는지를 아는 것 자체가 실행 순서를 묻는 질문에서 자주 등장합니다.
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 1000;면접관이 확인하는 방식
면접관은 열에 함수를 적용해 느려진 쿼리를 보여 주고, 결과를 바꾸지 않으면서 빠르게 만들어 보라고 합니다. 이때 다음과 같이 대응하십시오.
- 열에 함수를 적용한 조건이 인덱스를 사용할 수 없음을 식별합니다
- 열을 그대로 유지하도록 범위 조건이나 상수 쪽 계산으로 다시 작성합니다
- 다시 작성할 수 없다면 함수 기반 인덱스나 저장된 계산 열을 제안합니다
EXPLAIN으로 실행 계획이 순차 스캔에서 인덱스 스캔으로 바뀌었는지 확인한다고 언급하면 답변이 완성됩니다.
트레이드오프 이해
균형 있게 답변하십시오. 인덱스와 함수 기반 인덱스는 읽기를 빠르게 하지만 쓰기를 느리게 하고 저장 공간을 사용합니다. 아주 작은 테이블에서는 전체 스캔도 괜찮으므로 인덱스를 추가하는 것은 낭비입니다.
숙련된 답변은 조건부입니다. 이 열이 크고 이런 방식으로 자주 필터링된다면 조건식이 인덱스를 사용할 수 있게 다시 작성하거나 함수 기반 인덱스를 추가하고, 그렇지 않다면 그대로 둡니다. 면접에서는 독단적인 원칙보다 맥락이 중요합니다.
함수 인덱스로 계산식을 검색 가능하게 만들기
때로는 대소문자를 구분하지 않는 일치처럼 변환된 값을 기준으로 필터링해야 할 때가 있습니다. 인덱스를 포기하는 대신, 필터링에 사용하는 정확한 표현식에 대해 표현식(함수 기반) 인덱스를 생성하십시오.
- 열을 함수로 감싸더라도 최적화기가 인덱스를 사용할 수 있습니다.
- 인덱스 표현식은 술어 표현식과 정확히 일치해야 합니다.
-- index the expression you filter on
CREATE INDEX idx_users_lower_email ON users (lower(email));
-- now this predicate stays sargable
SELECT * FROM users WHERE lower(email) = 'amy@example.com';빠른 확인
최적화기가 인덱스를 사용할 수 있는 조건식을 찾아보십시오.
정리
핵심 내용:
- 인덱싱된 열이 함수나 산술 연산 안이 아니라 그대로 나타나면 조건식은 검색 가능합니다
YEAR(col) = 2024를 반열린 범위로 다시 작성하고, 계산을 상수 쪽으로 옮깁니다- 피할 수 없는 식에는 함수 기반 인덱스나 저장된 계산 열을 사용합니다
SELECT별칭은WHERE에서 사용할 수 없으며, 집계는HAVING에 넣습니다
전형적인 질문은 느린 쿼리이고, 전형적인 해결책은 열을 그대로 유지하는 것입니다.
자주 묻는 질문
“계산된 값으로 필터링하기” 강의는 무료인가요?
네 — “계산된 값으로 필터링하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 SQL Interview Prep 강의 전체를 잠금 해제할 수 있습니다. SQL Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
“계산된 값으로 필터링하기”에서 뭘 배우나요?
열에 함수를 적용하면 인덱스 사용이 중단되는 이유와 면접관이 이를 확인하는 방법을 알아봅니다. 브라우저에서 직접 실행하는 실습 코드로 SQL Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
SQL Interview Prep을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 SQL Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“계산된 값으로 필터링하기” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 SQL Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 SQL Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.