MSSQL `datetime` 반올림 이슈 대응
MSSQL의 datetime 타입은 밀리초 단위로 정밀하게 저장되지 않는다.
datetime은 1초를 300단계로 나누는 방식(=약 3.33ms 단위)을 사용하기 때문에
0.003초, 0.007초 단위에서 반올림이 발생한다.
예를 들어 다음과 같은 경우:
| 입력값 | 실제 저장값 |
|---|---|
2025-10-28 23:59:59.996 |
그대로 저장 |
2025-10-28 23:59:59.997 |
반올림되어 2025-10-29 00:00:00.000 으로 저장 |
즉, 23:59:59.997 이후의 시각은 다음날 00:00:00 으로 반올림됩니다.
이 문제로 인해 날짜 범위 검색 시,
BETWEEN '2025-10-28 00:00:00' AND '2025-10-28 23:59:59.997'
처럼 작성해도 데이터가 누락되는 상황이 발생할 수 있습니다.
해결 방법
datetime2타입으로 변경datetime2는 최대 100ns(나노초) 단위로 정밀하며, 반올림 문제가 없음.- 새 시스템이라면 무조건
datetime2사용을 권장.
- 시간 보정 로직 추가
- 기존 DB가
datetime이라면, 끝나는 시각을 1초 늘려 검색 범위를 안전하게 확보한다.LocalDateTime endDT = range.getEndDT().plusSeconds(1);
- 기존 DB가
왜 BETWEEN 대신 >= / < 조건을 썼을까?
<foreach collection="searchDto.startDTRanges" item="range" separator=" OR ">
("start_dt" >= #{range.startDT} AND "start_dt" < #{range.endDT})
</foreach>
SQL에서 날짜나 시간 범위를 조회할 때 많은 개발자들이 자연스럽게 BETWEEN을 사용한다.
하지만 실무에서는 경계값 중복 문제 때문에 BETWEEN 대신 >= / < 조합을 사용하는 것이 더 안전하다.
- BETWEEN은 양쪽 끝을 포함(inclusive) 한다
- 경계 포함 여부를 명시적으로 제어하기 위함
- MSSQL datetime 반올림 이슈와도 연결됨
- OR 조건과 함께 사용할 때도 안전
| 항목 | BETWEEN |
>= / < 조합 |
|---|---|---|
| 포함 여부 | 양쪽 포함 (inclusive) | 시작 포함 / 끝 미포함 |
| 날짜 경계 중복 | 발생 가능 | 없음 |
| MSSQL datetime 반올림 대응 | 취약 | 안전 |
| 여러 Range OR 조건 시 | 중복 포함 위험 | 안정적 |
| 명확성 | 모호 (자동 포함) | 명확 (의도 드러남) |