FOREST_CHOI's BLOG

JPA OrderBy rand() Issue 본문

프로그래밍/JPA

JPA OrderBy rand() Issue

Forest_Choi 2022. 11. 8. 01:51
728x90

Querydsl 을 사용하면서 랜덤값을 추출 해야할 경우가 생겼다.

 

일단 나는 아래와 같이 native Query를 Querydsl에도 적용하려고 해 보았다.

@Query(value = "select * from building order by RAND() limit 1", nativeQuery = true)
    public Optional<Building> findBuilding();
public Optional<Building> findBuilding() {
        Building building = jpaQueryFactory
        	.selectFrom(QBuilding.building)
	        .orderby({_____})
    	   	.limit(1)
	        .fetch();
        return Optional.ofNullable(building);

    }

orderby 중괄호가 된 부분에 building.id.rand() 형태로 넣어 줄 수 있는 지 알았다.

하지만, jpa에서 제공하지 않는 문법이기 때문에 당연히 querydsl에서 도 지원을 하지 않는 것 이었다.

 

결정적으로 일단 이정도만 지원한다.


하지만 저렇게 rand하여 값을 가져오는데는 문제가 있다는 결론에 달았다.

일단,  첫번 째 native query를 다시 볼 필요가 있다.

@Query(value = "select * from building order by RAND() limit 1", nativeQuery = true)

일단 기본적으로 RAND() 함수는 쿼리 실행 순간에 레코드에 각각 임의의 값을 할당 후 그 값으로 정렬을 수행하게 되는데, 비용이 아주 큰연산이다. 또한 order by 조건에 인덱스를 활용할 수 있는 조건이 아니면 모든 조건은 비효율적이라고 생각하면 된다.

 

** 결국 비효율적인 방법이라는 것이다.!!!

 

이에 대한 해결책으로 CountAll 하여 row의 개수를 가져오고 여기서 Java의 Random함수를 통해 랜덤값을 가져올 수 있는 방법을 택하였다.

public Optional<Building> findRandomBuilding(){
	Long count = buildingRepository.countAll();
	int randomIndex = (int)(Math.random() * count);

	return Optional.ofNullable(jpaQueryFactory
                    .selectFrom(QBuilding.building)
                    .where(building.id.eq(randomIndex))
                    .limit(1)
                    .fetch());
}

그나마 개선한 작업이 이 메서드였다. 랜덤한 pk로 값을 가져올 수 있어서 될 것 같았지만 만약 삭제된 Pk라면?????? null을 반환하게 될 것이다. ㅠㅠ

 


결국 마지막에는 구글링을 통해 가져 왔는데, 페이징 처리를 통하여 랜덤 페이지 1개를 찾고 그 중 하나의 값을 가져오는 로직으로 바꾸었다.

public Optional<Building> findRandomBuilding(){
	Long count = buildingRepository.countAll();
	int randomIndex = (int)(Math.random() * count);
	
    Page<Building> buildingPage = questionRepository.findAll(new PageRequest(idx, 1));
    Building building = null;
    if (buildingPage.hasContent()) {
        building = buildingPage.getContent().get(0);
    }
    return Optional.ofNullable(building);
}

아래 StackOverFlow에서 정보를 얻을 수 있었다.

 

https://stackoverflow.com/questions/24279186/fetch-random-records-using-spring-data-jpa

 

Fetch random records using Spring data JPA

I want to fetch random records using Spring data JPA. I was using @Query for the same.But it is taking a long time. @Query("select que from Question que order by RAND()") public List<Question>

stackoverflow.com

 

728x90
Comments