QueryDSL로 날짜별 생성된 데이터를 세는 쿼리를 구현했다.
그런데 내가 생각한 것과 데이터 개수가 다르게 나오는 것이다...
(왜그럴까)
1. 문제 상황
Tuple results = queryFactory.select(qStatsTable.regDt, qStatsTable.count())
.from(qStatsTable)
.orderBy(qStatsTable.regDt.desc())
.groupBy(qStatsTable.regDt)
.limit(1)
.fetchOne();
stats_table에서 reg_dt를 기준으로 그룹화하여 내림차순으로 정렬한 뒤 select 하는 QueryDSL이다.
reg_dt는 등록일자를 의미하는 컬럼인데 내가 생각한 결과는 날짜별로 count 개수를 세는 것이다.
SELECT reg_dt, COUNT(*)
FROM stats_table
GROUP BY reg_dt
ORDER BY reg_dt DESC
LIMIT 1;
QueryDSL을 쿼리문으로 바꾼 뒤 실행한 결과는 다음과 같았다.
이 결과가 나온 이유는 reg_dt는 TimeStamp로 정의되어 있기 때문에
초단위 시간까지 일치하는 reg_dt끼리 그룹화되어버린 것이다.
내가 원하는 결과는 reg_dt가 일 단위까지 끊어서 그룹화 하는 것이었다.
2. 해결 방법
그래서 수정한 쿼리문은 다음과 같다.
SELECT DATE(reg_dt) AS reg_dt, COUNT(*)
FROM stats_table
GROUP BY DATE(reg_dt)
ORDER BY reg_dt DESC
LIMIT 1;
reg_dt를 DATE로 감싸면 TimeStamp였던 형식을 Date 형식으로 변환하면서,
내가 원하는 일 단위 기준의 데이터 개수를 얻게 되었다!
자 이렇게 쿼리문으로 어떻게 짜야 하는지 감을 잡았으니 이제 QueryDSL에 적용해보자.
다양한 방법이 있곘지만 내가 사용하고 있는 데이터베이스는 Cubrid(큐브리드)이기 때문에,
(큐브리드가 지원할 수 있는) Expressions.dateTemplate을 이용하여 SQL 표현식을 작성할 수 있다.
Expressions.dateTemplate는 QueryDSL에서 날짜 형식을 지정하여 SQL 구문을 작성할 때 사용한다.
기본적인 형식은 이렇게 생겼다.
Expressions.dateTemplate(Date.class, "DATE({0})", qEntity.dateField)
1) 타입(Date.class) : 반환될 타입을 지정한다. TimeStamp -> Date 변환이 필요하면 Date.class를, TimeStamp -> String 변환이 필요하면 String.class를 입력하면 된다.
2) 패턴("DATE({0})") : 날짜 형식을 지정하는 템플릿 문자열이다. DATE 함수를 통해 {0}에는 qEntity.dateField가 들어간다.
3) 값(qEntity.dateField) : 템플릿에서 사용될 값들을 전달한다.
이렇게 하면 시간 부분은 제외한 날짜만 추출할 수 있다.
예를 들어, 2025-09-15 12:30:45 에서 2025-09-15 만 추출할 수 있다.
날짜 형식을 DATE_FORMAT을 이용하여 직접 지정할 수도 있다.
Expressions.dateTemplate(Date.class, "DATE_FORMAT({0}, {1})", qEntity.dateField, "yyyy-MM-dd")
Query문으로 돌아가는 걸 확인했으니 QueryDSL에 적용시켜보자.
Tuple results = queryFactory.select(qStatsTable.regDt, qStatsTable.count())
.from(qStatsTable)
.groupBy(Expressions.dateTemplate(String.class, "DATE({0})", qStatsTable.regDt))
.orderBy(qStatsTable.regDt.desc())
.limit(1)
.fetchOne();
select 조건은 유지한채로 groupBy 문을 바꿔주었다.
기존에 qStatsTable.regDt 기준에서 qStatsTable.regDt을 DATE 형식으로 변환한 형태로 바꿔주었다.
3. 주요 Expressions 종류
1) Expressions.constant()
- 상수 값을 쿼리에서 사용할 때 사용한다.
Expressions.constant("Hello")
2) Expressioins.template()
- 템플릿 문자열을 이용하여 표현식을 생성한다.
Expressions.template(String.class, "CONCAT({0}, {1})", qEntity.firstName, qEntity.lastName)
3) Expressions.dateTemplate()
- 날짜 형식을 지정할 때 사용한다.
Expressions.dateTemplate(Date.class, "DATE_FORMAT({0}, {1})", qEntity.dateField, "yyyy-MM-dd")
4) Expressions.booleanTemplate()
- Boolean 타입을 처리할 수 있는 템플릿 표현식을 생성한다.
Expressions.booleanTemplate("{0} = {1}", qEntity.status, 1)
5) Expressions.numberTemplate()
- 숫자 타입 표현식을 생성할 때 사용한다.
Expressions.numberTemplate(Integer.class, "{0} + {1}", qEntity.num1, qEntity.num2)
6) Expressions.stringTemplate()
- 문자열 타입 표현식을 생성할 때 사용한다.
Expressions.stringTemplate("CONCAT({0}, {1})", qEntity.firstName, qEntity.lastName)
7) Expressions.path()
- Path 객체를 생성할 때 사용한다.
Expressions.path(String.class, "name")
8) Expressions.predicate()
- Predicate 객체를 생성할 때 사용한다.
Expressions.predicate(Ops.EQ, qEntity.status, 1)
저장할 때 Timestamp로 저장했기 때문에 groupBy도 초 단위로 그룹화하는 것을 놓쳤었다.
Expressions을 활용해서 다양한 조건을 가지고 있는 변수를 다룰 수 있었다.
'Development > Spring' 카테고리의 다른 글
[Apache POI] Iterator로 읽은 빈 셀 CellType.BLANK로 예외처리하기 (0) | 2025.09.16 |
---|---|
[Apache POI]Java에서 이미지를 포함한 Excel 파일 생성하기 (5) | 2025.08.08 |
[SpringBoot] select 쿼리문에서 update가 일어났던 이유 (1) | 2025.06.18 |