Programming-[Backend]/SQL

[TIL] groupBy 제거 필요 시 집계함수 활용, XOR 조건문 사용하기

컴퓨터 탐험가 찰리 2022. 5. 2. 20:12
728x90
반응형

 

1. groupBy 제거 필요 시 집계함수 활용

 

상황

querydsl에서 CaseBuilder를 사용하여 select 절을 구현하는 부분에서, groupBy를 하고 싶지 않은데 then절에 포함되어 groupBy를 지정해주어야만 했다.

 

new CaseBuilder().when(ordersEntity.id.countDistinct().eq(1L))
        .then(ordersEntity.id)
        .otherwise(0L).as("orderId")
        
        //중략...
        
        .groupBy(
        ...
        ordersEntity.id
        )

 

ordersEntity의 개수가 1개일때만 then절의 ordersEntity.id를 뽑아오는데, 그럼 select절에 ordersEntity.id가 있는 셈이므로 groupBy에 작성해주어야만 한다. 그런데 이렇게 groupBy문이 있으면 아래 엔티티 그림과 같이 1개의 Member에 1개의 Order가 있는 경우 각각의 ordersEntity.id로 분리되어 조회된다.

 

다시 말해 Member 1개에 엮인 Order가 여러 개인 경우 C와 같은 결과가 나오길 원하는 상황인데 그러질 못하는 것이다. 조회 시 B와 같이 Orders.id에 따라 groupBy문으로 분리되기 때문이다.

 

방법

groupBy문을 제거한다. 그럼 orders.id = 1, orders.id = 2,... 와 같이 분리되어 표시되지 않는다. 문제는 조건문에서 사용한 

 

.then(ordersEntity.id)

 

값이다. ordersEntity가 1개만 존재할 때는 select문에 해당 id값이 들어가야되므로 반드시 groupBy가 있어야한다. 이때 집계함수 문법을 사용하여 ordersEntity.id.max()를 해준다. 1개만 존재할 때의 조건이므로 어짜피 ordersEntity.id 값은 1개이고, max를 하더라도 변하지 않는다. 그러면서 Orders가 여러 개일때는 groupBy를 해제하여 원하는 결과를 얻게 해준다.

 

상황상 ordersEntity.id가 1개일 조건만 걸어줬지만, 만약 max등 대표값으로 표현이 가능한 상황이라면 여러 결과가 도출되는 경우에도 집계함수를 활용할 수 있을 것 같다.

 

 

 

 


 

 

2. XOR 조건문 사용하기

 

상황

메인 쿼리 내부에 서브 쿼리를 적어주는 상황에서, XXEntity의 price 값이 null일 수 있는 상황이였다.

 

---으로 표기된 부분 사이의 조건문이다. 단순히 XXEntity.price == subXXEntity.price로 적어주면 안된다! 이 경우에는 XXEntity.price값이 null이라면 null == null로 비교해주지 않는다!!

 

그리고 XXEntity.price는 어떤 값이 아니라 JPA에서 사용하는 Path 타입이므로 null의 직접적인 검사가 어렵다.

//Main 쿼리문...
...,

//서브 쿼리(스칼라)
JPAExpressions
        .select(...sum().longValue().coalesce(0L).as("qty"))
        .from(...)
        .innerJoin(...)
        .innerJoin(...)

        .where(
                AlphaEntity.id.eq(subAlphaEntity.id),
                BetaEntity.id.eq(subBetaEntity.id),
                GammaEntity.id.eq(subGammaEntity.id),
                //-----------
                (
                        (XXEntity.price.isNull().and(subXXEntity.price.isNull()))
                                .or(XXEntity.price.isNotNull().and(XXEntity.price.eq(subXXEntity.price)))
                ),
                ...
                //------------
        )
        
//... 생략

 

해결

XOR 문법을 사용한다. 컴퓨터 공학에서 배운 아이디어가 떠오르다니 재밌다. XOR은 배타적 논리합으로 A, B 두 조건 중 1개가 참일 경우만 참이된다. 즉 XXEntity, subXXEntity의 price가 null 이거나, XXEntity.price가 is not null이고 XXEntity.price와 subXXEntity.price값이 같을 때를 or 조건으로 검사하면 된다.

 

위 예제가 정확히 아래 공식을 대변하지는 않지만, 이렇게 배타적 논리합으로 조건을 생각하면 null 검사를 할수도 있다.

 

XOR = ((not a) and b) or (a and (not b))

728x90
반응형