Scott 과 함께 - Cross Join 을 Java 로 구현하기

Cross Join in Java

Cross Join with Java

 이번 Post는 Cross Join을 Java로 구현한다. Cross Join은 조건없이 테이블 2개를 합치는 Join이다. 조건이 없다보니 데이터는 2개 테이블의 행 개수의 곱셈이다. 만약 A 테이블(2건)과 B 테이블(3건)을 Cross Join한다면, 행 개수는 2 x 3 = 6 이다.

 그런데 궁금한 점은 이런 Join은 어디에 사용하는가? 개인적으로 집계 데이터의 Driving Table로 사용한다. Scott 도메인에서 예를 만든면, '모든 부서 x 모든 Job'의 급여평균을 구할 때 사용할 수 있다. 그래서 경험상 보면, Cross Join은 단독으로 사용하지 않고 Outer Join과 같이 사용한다.

SQL

with job as (
  select distinct emp.job as job_name
    from emp
)
select dept.dname,
       job.job_name,
       avg(emp.sal)
       from job
       cross join dept
       left outer join emp
       on (job.job_name = emp.job and dept.deptno = emp.deptno)
 group by dept.dname,
       job.job_name
 order by dept.dname,
          job.job_name

   아래 그림처럼 부서에 Job이 없더라도 같은 형식을 유지하기 위해서 Cross Join을 사용한다. 여기서 알아야 할 이슈가 있는데, Cross Join으로 만들어진 Driving Table은 Index가 없기 때문에 Join에 대한 Cost가 높다. 그래서 Driving Table의 데이터는 적을 수록 좋다.

Scott Outer Join Result

JAVA

 이번에 Stream의 flatMap()을 사용해서 Cross Join을 구현한다.

flatMap()

Java Stream Flatmap Example

 Stream에서 flatMap()은 다차원 배열의 값을 1차원으로 만들어준다. 만약 3 x 3 행렬에 모든 성분의 합을 구할 때 이중 Loop를 쓸수 있지만, flatMap()으로 1차원으로 평탄화시켜 합계를 구할 수 있다.

@Test
public void test() {
    Integer[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

    Integer integer = Arrays.stream(matrix)
            .flatMap(Arrays::stream)
            .reduce(Integer::sum)
            .get();

    System.out.println(integer);
}

Cross Join in flatMap

flatMap DTO
Cross Join with DTO

 Cross Join에서 flatMap()을 응용한다. Department List와 EmployeeJob Enum을 합쳐 모든 부서/Job의 경우의 수를 만든다. 경우의 수는 DTO에 저장한다.

@Test
public void crossJoin_Stream() {
  ...
  sql = "select d from Department d";
  // 모든 부서와 Job 은 Cross Join으로 데이터를 가져온다.
  List<DeptJobCountDto> dtos = entityManager.createQuery(sql, Department.class)
      .getResultStream()
      .flatMap(x -> Arrays.stream(EmployeeJob.values())
          .map(t -> new DeptJobCountDto(x.getName(), t)))
  ...
}

DTO의 결과와 Inner join으로 만든 부서/직업별 평균급여를 합친다.

결과

마치며

 이것으로 Join 3편을 마무리한다. (아무도 몰랐겠지만...시리즈였다.) 더 발전시킨 Code나 비즈니스 케이스가 있다면 추가 Post를 작성할 예정이다. 소스는 Github를 참조.

참조

댓글

이 블로그의 인기 게시물

JPA 와 함께 - 느낀점

Scott 과 함께 - Recursive Query 구현하기