• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

Querydsl random select 쿼리

23.05.11 18:09 작성 23.05.11 18:14 수정 조회수 976

0

https://www.inflearn.com/questions/284950/querydsl-random-select%EA%B4%80%EB%A0%A8

2년정도 전에 Querydsl에서 ramdom select는 지원하지 않는다고 하셨는데 지금은 찾아보니 Expressions.numberTemplate()을 통해 SQL Function을 사용할 수 있다고 하는데 권장하는 사용법인지, 올바른 random select 방법인지 궁금합니다.


추가로 같은 결과를 반환하는 random select, distinct, limit, 프로젝션을 한번에 수행하는 2개의 방법인데 2개의 차이는 다음과 같고 어느것을 추천하시는지 궁금합니다.

case1의 방법이 limit를 가져오기에 불필요한 count쿼리를 추가로 날리지 않지만 실행 시간이 더욱 큽니다.

case1 - Querydsl 사용

테스트 실행시간 : 78ms

count 쿼리를 추가로 생성하지 않음

public List<QuizWordDto> findMyWordRandomForQuiz() {
        return queryFactory
                .select(Projections.constructor(QuizWordDto.class, myWord.name, myWord.morpheme, myWord.mean))
                .distinct()
                .from(myWord)
                .orderBy(Expressions.numberTemplate(Double.class, "RAND()").asc())
                .limit(40)
                .fetch();
    }

 

case2 - 페이징을 이용하여 Limit 가져오기

테스트 실행시간 : 22ms

count 쿼리를 추가로 생성

@Query(value = "SELECT DISTINCT new com.ll.kotudy.word.service.dto.QuizWordDto(mw.name, mw.morpheme, mw.mean) " +
            "FROM MyWord mw " +
            "ORDER BY FUNCTION('RAND')", nativeQuery = false)
    Page<QuizWordDto> findDistinctRandomQuizWords(Pageable pageable);


테스트 코드

@BeforeEach
void init() {
    for (int i = 0; i < 50; i++) {
        myWordRepository.save(new MyWord("동물" + i, "명사",
                "사람을 제외한 길짐승, 날짐승, 물짐승 따위를 통틀어 이르는 말." + i));
    }
}

@Test
void findMyWordForQuiz() {
    long startTime = System.currentTimeMillis();

    PageRequest pageRequest = PageRequest.of(0, 40);
    Page<QuizWordDto> distinctRandomQuizWords = myWordRepository.findDistinctRandomQuizWords(pageRequest);

    long endTime = System.currentTimeMillis();
    System.out.println(String.format("코드 실행 시간: %20dms", endTime - startTime));
}

@Test
void findMyWordForQuiz_querydsl() {
    long startTime = System.currentTimeMillis();

    List<QuizWordDto> myWordRandomForQuiz = myWordRepository.findMyWordRandomForQuiz();

    long endTime = System.currentTimeMillis();
    System.out.println(String.format("코드 실행 시간: %20dms", endTime - startTime));
}

답변 1

답변을 작성해보세요.

1

안녕하세요. SIUUUUE님

Querydsl을 사용하더라도 네이티브 SQL의 함수를 그대로 호출할 수는 없습니다.

Querydsl은 결국 JPQL로 실행되기 때문에 네이티브 SQL의 기능을 기대로 사용할 수는 없는데요.

대신에 Hibernate 방언에 필요한 함수를 추가하거나 또는 이미 등록되어 있는 경우에는 호출할 수 있습니다.

이 경우 rand() 함수가 해당 DB를 사용하는 방언에 추가되어 있어서 사용할 수 있습니다.

Expressions.numberTemplate은 사용하셔도 됩니다. 물론 이 경우도 앞서 말씀드린 것 처럼 JPQL이 지원하는 기능에 한정해서 호출이 가능합니다.

당시 질문을 이해했을 때는 단순히 이런 함수를 호출하는 수준이 아니라 특정 DBMS에 종속적인 SQL 문법 기능을 호출한다고 이해해서 네이티브 쿼리를 사용하는 것으로 말씀드렸습니다.(그리고 실제 너무 복잡하게 고민하기 보다는 애매한 경우에는 네이티브 쿼리를 사용하는 것을 권장합니다.)

두번째 질문하신 부분은 용도에 따라서 다릅니다. 예를들어서 페이징에서 마지막 페이지 숫자를 보여줘야 한다면 count 쿼리를 통해서 전체 숫자를 얻어와야 합니다.

감사합니다.

SIUUUUE님의 프로필

SIUUUUE

질문자

2023.05.14

답변 감사합니다!