스프링데이터 + JPA/QueryDSL

13. 그룹

sdafdq 2023. 11. 29. 10:36

group by, having 이런 거.

지금 시간들은 일단 1차적으로, QueryDSL의 사용방법, 즉 문법을 알아보는 시간임.

 

@Test
public void aggregation(){
    List<Tuple> result = query.select(m.count(), m.age.sum(), m.age.avg(), m.age.max(), m.age.min())
            .from(m)
            .fetch();

    Tuple tuple = result.get(0);
    assertThat(tuple.get(m.count())).isEqualTo(4);
    assertThat(tuple.get(m.age.sum())).isEqualTo(100);
    assertThat(tuple.get(m.age.avg())).isEqualTo(25);
    assertThat(tuple.get(m.age.max())).isEqualTo(40);
    assertThat(tuple.get(m.age.min())).isEqualTo(10);
}

select 절로 원하는 것을 딱 집을 경우 QueryDSL이 따로 만든 Tuple 타입으로 제공한다.

튜플이란 것은 기본적으로, 수량이 딱 정해진 것의 집합, 모듬이다.

원소 개수는 유한하며, 순서가 정해져있습니다.

 

QueryDSL에서 튜플을 쓰는 이유는, 타입이 여러 개 들어오기 때문.

 

사실 실무에서 튜플을 많이 쓰지는 않고, 보통 Dto로 직접 뽑아오는 방식을 쓴다.

 

코드에 대한 설명을 하자면, count등 모두 group에 대한 쿼리이기 때문에, row가 하나만 나올 것이다.

 

그래서 get(0)을 했다. 그냥 fetchOne() 해도 되긴 한다.

테스트는 통과 했으며,

쿼리는

/* select
    count(member1),
    sum(member1.age),
    avg(member1.age),
    max(member1.age),
    min(member1.age) 
from
    Member member1 */ select
        count(m1_0.member_id),
        sum(m1_0.age),
        avg(cast(m1_0.age as float(53))),
        max(m1_0.age),
        min(m1_0.age) 
    from
        member m1_0

이렇게 나간다.

 

튜플에서 가져올 때는 QueryDSL의 표현식이란 걸 써서 가져온다.

 

//팀의 이름과 각 팀의 평균연령
@Test
public void group(){
    List<Tuple> fetch = query.select(t.name, m.age.avg()).from(m).join(m.team, t).groupBy(t).fetch();
    Tuple tuple1 = fetch.get(0);
    Tuple tuple2 = fetch.get(1);

    assertThat(tuple1.get(t.name)).isEqualTo("teamA");
    assertThat(tuple1.get(m.age.avg())).isEqualTo(15);

    assertThat(tuple2.get(t.name)).isEqualTo("teamB");
    assertThat(tuple2.get(m.age.avg())).isEqualTo(35);
}

이 코드는 팀의 이름과 각 팀의 평균 연령을 구하는 코드이다.

SQL 코드는 기본적으로 from절 부터 보며, 맨 마지막에 select 절을 본다.

m을 모두 가져오고, t를 join해서 m.team(테이블 상에서는 외래키)와 t(테이블 상에서는 식별키)를 맞춰서 또 t를 가져오며,

그렇게 m을 모두 가져오고, 이어서 t를 모두 가져온 다음에,

t의 식별키를 기준으로 groupby를 한다.

그 다음 select 절을 봤더니 age()함수가 있다. groub by를 해놨으므로 그걸로 한다. t.name과 함께 결과를 가져온다.

나는 group by를 t, 즉 team의 식별키로 했는데, 확실히 t.name을 가져올거면 t.name으로 groub by 를 하는 게 좋아보인다.

 

나는 데이터 무결성을 위해 식별키로 했다.

 

결과는 잘 통과했다.

/* select
    team.name,
    avg(member1.age) 
from
    Member member1   
inner join
    member1.team as team 
group by
    team */ select
        t1_0.name,
        avg(cast(m1_0.age as float(53))) 
    from
        member m1_0 
    join
        team t1_0 
            on t1_0.team_id=m1_0.team_id 
    group by
        t1_0.team_id

나간 쿼리

 

참고로 groupBy() 이후 SQL처럼 having() 해서 그룹의 조건도 더 넣을 수 있다.

.groupBy(item.price)
.having(item.price.gt(1000))

having이란 

https://qwefdg3.tistory.com/720

 

select (where 포함)

 

qwefdg3.tistory.com

where 해서 가져오는 것 처럼, group들에 들어갈 것을 특정 조건식으로 필터링 할 수 있다.

 

 

 

'스프링데이터 + JPA > QueryDSL' 카테고리의 다른 글

15. join on  (0) 2023.11.29
14. join  (0) 2023.11.29
12. 페이징  (0) 2023.11.28
11. 정렬  (0) 2023.11.28
10. 결과 조회  (0) 2023.11.28