조건식과 필터링
BETWEEN, IN, LIKE를 값의 모양에 맞게 선택하고 경계와 와일드카드 오류를 검증합니다.
30회에서 60회 사이 게시글, 여러 상태 중 하나, 제목이 특정 접두사로 시작하는 기록은 모두 필터지만 값의 모양이 다릅니다.
모든 조건을 긴 OR와 비교식으로만 쓰면 의미가 흐려지고 경계를 빠뜨리기 쉽습니다.
이 문서는 연속 구간에는 BETWEEN, 이산 목록에는 IN, 문자열 패턴에는 LIKE를 사용합니다.
문법을 줄이는 것이 목적이 아니라, 요구사항의 구조를 SQL 연산자에 그대로 보이게 만드는 것이 목적입니다.
무분별한 OR 조건
공개 또는 보관된 게시글 중 30~60회 기록을 찾으면서 제목이 SQL로 시작해야 한다고 합시다.
괄호 없이 OR를 섞으면 공개 게시글에는 나머지 조건이 적용되지 않는 결과가 생깁니다.
또 LIKE '%SQL%'을 사용하면 접두사라는 요구보다 넓은 행이 섞입니다.
SELECT id, title, status, view_count
FROM query_post_demo
WHERE status = 'PUBLISHED'
OR status = 'HIDDEN'
AND view_count BETWEEN 30 AND 60
AND title LIKE '%SQL%';AND가 먼저 평가되므로 모든 공개 게시글이 시간과 제목 조건을 건너뜁니다.
다음처럼 10회짜리 공개 게시글이 포함되면 실행은 성공했어도 의미가 오류가 난 것입니다.
오류 실행 결과 중 핵심 행id | title | status | views
7 | 설치 메모 | PUBLISHED | 10
2 | SQL WHERE | PUBLISHED | 50
8 | 고급 SQL 복습 | HIDDEN | 45
9 | SQL 함수 | HIDDEN | 30오류 쿼리의 첫 번째 PUBLISHED 분기는 제목과 조회수 조건을 모두 건너뛰므로 fixture의 나머지 게시된 행도 함께 나옵니다.
위 표는 조건 누락과 과도한 %SQL% 범위를 동시에 보여 주는 행을 추려 적은 것입니다.
SQL은 줄바꿈이나 들여쓰기로 논리를 바꾸지 않습니다.
괄호가 없으면 A OR B AND C는 A OR (B AND C)로 읽힙니다.
%SQL%은 앞뒤에 어떤 문자열이든 허용하므로 고급 SQL 복습도 통과합니다.
요구사항의 ‘SQL로 시작’은 SQL%가 정확합니다.
조건 형태와 연산자
BETWEEN a AND b는 두 경계를 모두 포함하는 연속 구간입니다.
날짜와 시각에서는 마지막 날 자정만 포함하는 실수를 피하기 위해 반열린 구간 >= start AND < next_start가 더 안전할 때가 많습니다.
IN은 같은 컬럼이 여러 이산 값 중 하나인지 표현하며 긴 OR보다 목록의 의도를 드러냅니다.
LIKE의 %는 0개 이상의 문자, _는 정확히 한 문자를 뜻합니다.
패턴은 데이터 값이 아니라 검색 규칙이므로 사용자 입력의 %와 _를 문자 그대로 찾을 때는 이스케이프 정책이 필요합니다.
BETWEEN, IN, LIKE는 표준 SQL에 속합니다.
MySQL의 문자열 패턴 대소문자 구분은 정렬 규칙에 영향을 받습니다.
LIKE 'SQL%'처럼 고정 접두사가 있는 패턴은 인덱스 범위를 사용할 여지가 있지만 LIKE '%SQL'처럼 선행 와일드카드가 있으면 일반 B-트리의 왼쪽 시작점을 정하기 어렵습니다.
실제 선택은 7장에서 EXPLAIN ANALYZE로 확인합니다.
목록·범위·패턴 필터
상태는 이산 목록, 시간은 포함 구간, 제목은 접두사 패턴으로 각각 표현합니다.
모든 조건이 AND로 연결되므로 괄호 우선순위도 단순해집니다.
SELECT id, title, status, view_count
FROM query_post_demo
WHERE status IN ('PUBLISHED', 'HIDDEN')
AND view_count BETWEEN 30 AND 60
AND title LIKE 'SQL%'
ORDER BY view_count DESC, id;공개 여부만으로 통과하지 않고 세 조건을 모두 만족한 행만 남습니다.
두 경계값도 포함됩니다.
개선 실행 결과id | title | status | views
2 | SQL WHERE | PUBLISHED | 50
9 | SQL 함수 | HIDDEN | 30IN 목록에 NULL을 넣어 ‘NULL도 포함’하려고 하면 의도대로 동작하지 않습니다.
NULL은 동등 비교 대상이 아니므로 column IN (...) OR column IS NULL처럼 별도 조건을 써야 합니다.
패턴도 사용자가 입력한 문자열을 그대로 붙이지 말고 파라미터 바인딩과 이스케이프 규칙을 적용해야 SQL 주입과 와일드카드 오해를 함께 피할 수 있습니다.
반개구간 시간 조건
2026년 7월 13일 하루를 찾을 때 BETWEEN '00:00:00' AND '23:59:59'로 쓰면 마이크로초가 있는 마지막 순간을 빠뜨릴 수 있습니다.
다음 날 시작 전까지라는 반열린 범위가 정밀도 변화에도 안전합니다.
SELECT id, title, created_at
FROM query_post_demo
WHERE created_at >= '2026-07-13 00:00:00'
AND created_at < '2026-07-14 00:00:00'
ORDER BY created_at, id;DATETIME(6)의 23:59:59.999999 행도 포함되고 다음 날 0시는 제외됩니다.
날짜 파라미터에서 다음 날을 계산하는 책임은 애플리케이션이나 명확한 날짜 함수로 처리하되, 컬럼에 함수를 씌워 비교하는 방식이 인덱스 사용에 어떤 영향을 주는지 함께 점검합니다.
필터 연산자를 고를 때는 결과 정확성과 탐색 가능성을 함께 봅니다.
예를 들어 LOWER(title) LIKE 'sql%'은 대소문자 정책을 쿼리 안에서 강제하지만 컬럼 표현식 때문에 기존 인덱스를 그대로 쓰기 어려울 수 있습니다.
먼저 적절한 정렬 규칙을 설계하고, 정말 표현식 검색이 필요하면 기능 인덱스나 생성 컬럼을 뒤에서 검토합니다.
경계 테스트는 표본 한두 행으로 끝내지 않습니다.
30~60회 조건이라면 29, 30, 60, 61회을 모두 넣고 포함 여부를 표로 기록합니다.
날짜 구간에는 시작 바로 전, 시작, 종료 직전, 종료를 넣습니다.
LIKE에는 빈 문자열, 접두사만 같은 값, 검색어가 중간에 있는 값, %와 _가 실제 문자로 들어간 값을 준비합니다.
IN 목록이 애플리케이션에서 동적으로 만들어진다면 빈 배열일 때 IN () 같은 잘못된 SQL을 만들지 않도록 호출 전 정책도 정해야 합니다.
‘조건 없음’으로 볼지 ‘결과 없음’으로 볼지는 제품 요구이며, SQL 문자열 생성기가 대신 결정하게 두면 안 됩니다.
같은 필터라도 검색 화면과 권한 필터는 오류 발생 시 기본 동작이 달라야 합니다.
검색 조건이 많아지면 ‘조건을 입력하지 않은 경우’를 NULL 파라미터와 섞어 동적 SQL로 만드는 일이 많습니다.
WHERE (:status IS NULL OR status = :status)는 편리하지만 선택도와 실행 계획이 여러 경우를 한 문장에 섞을 수 있습니다.
요청 조합이 제한적이라면 필요한 조건식만 조립하고 바인딩 값을 따로 전달하는 방식이 읽기와 튜닝에 유리할 수 있습니다.
LIKE 검색어의 길이 제한도 필요합니다.
한 글자 포함 검색은 거의 모든 행을 읽을 수 있고, 사용자에게 무제한 와일드카드를 허용하면 비용을 예측하기 어렵습니다.
최소 글자 수, 접두사 검색 우선, 전문 검색 도입 기준을 제품 수준에서 정한 뒤 SQL은 그 규칙을 정확히 구현해야 합니다.
NOT IN과 NOT BETWEEN, NOT LIKE는 단순히 화면의 체크박스를 뒤집는 기능이 아닙니다.
NULL 허용 값은 여전히 알 수 없음으로 남아 반대 집합에 들어오지 않을 수 있습니다.
제외 검색을 만들 때는 원래 집합, 포함 집합, NULL 집합을 각각 세어 전체가 어떻게 나뉘는지 확인합니다.
문자열 끝의 공백과 Unicode 정규화도 사용자 눈에는 같지만 비교 결과가 다를 수 있습니다.
검색 품질 요구가 높다면 DB 정렬 규칙 하나로 해결하려 하지 말고 입력 정규화와 검색 전용 인덱스의 책임을 따로 설계합니다.
시간대가 있는 사용자 날짜를 서버 UTC 경계로 바꿀 때 일광 절약 시간과 지역 설정도 테스트합니다.
필터 연산자 선택 기준
| 판단 축 | 확인할 질문 |
|---|---|
| 연속 범위 | 하한과 상한이 포함인지 제외인지 명확한가? |
| 이산 목록 | 같은 컬럼의 여러 값을 IN으로 표현할 수 있는가? |
| 문자 패턴 | 접두사·접미사·포함 중 실제 요구가 무엇인가? |
| NULL | 목록 비교와 별개로 IS NULL이 필요한가? |
| 입력 | 와일드카드와 파라미터를 안전하게 처리했는가? |
짧은 문법보다 경계 테스트가 중요합니다.
최소값, 최대값, 바로 바깥값, 빈 목록, NULL, %가 포함된 사용자 입력을 테스트 데이터로 만들어 결과 집합을 확인합니다.
연습 문제
7월 10일부터 7월 12일까지 시작된 게시글 중 상태가 DRAFT 또는 PUBLISHED이고 제목이 MySQL로 시작하는 행을 찾으세요.
created_at은 DATETIME(6)이며 7월 12일 밤의 모든 시각을 포함해야 합니다.
해설과 예시 답안
날짜는 7월 10일 이상, 7월 13일 미만으로 작성하면 12일의 모든 정밀도를 포함합니다.
상태는 IN, 제목은 고정 접두사 패턴을 사용합니다.
SELECT id, title, status, created_at
FROM query_post_demo
WHERE created_at >= '2026-07-10 00:00:00'
AND created_at < '2026-07-13 00:00:00'
AND status IN ('DRAFT', 'PUBLISHED')
AND title LIKE 'MySQL%'
ORDER BY created_at, id;7월 13일 0시 행이 제외되는지, 제목 중간에만 MySQL이 있는 행이 제외되는지 확인합니다.
이 두 경계가 통과하면 날짜와 패턴의 의미를 올바르게 옮긴 것입니다.
핵심 정리
- BETWEEN은 양끝을 포함하므로 날짜·시각 정밀도에서는 반열린 범위도 검토합니다.
- IN은 같은 컬럼의 이산 목록을 표현하고 NULL은 별도 조건으로 처리합니다.
- LIKE의 와일드카드 위치가 검색 범위와 인덱스 가능성을 바꿉니다.
- 복합 조건은 괄호와 연산자 우선순위를 업무 문장에 맞춥니다.
다음 문서에서는 필터를 통과한 결과의 순서와 개수를 안정적인 규칙으로 고정합니다.