2016-08-09 4 views
0

아래에서 예상되는 결과를 생성해야합니다. 기본적으로 특정 기간 (매주, 월간 등)에 따라 값을 집계하는 쿼리입니다. 시작과 끝이있는 날짜 필터가 있으며 모든 범위의 값을 반환해야합니다. 존재하지 않으면 0을 반환해야합니다.JPA 기간 쿼리를 처리하는 올바른 방법은 무엇입니까?

아래 예에서 시작 날짜는 '2015-08-02'이고 날짜는 '2015-08-23'이고 기간은 WEEKLY입니다. 2 주째에는 값이 없지만 0 값이 반환되어야합니다.

그래서이 경우 JPA를 사용하여 가장 좋은 방법은 무엇입니까? 우리는 임시 테이블을 사용하여이 테이블로 결과를 결합하여 전체 범위에 대한 결과를 얻지 만, JPA를 사용하여이 작업을 수행 할 수 있는지 여부는 알지 못합니다. 왜냐하면 테이블을 만들고 조인 한 다음 임시 테이블을 삭제해야하기 때문입니다.

또 다른 옵션은 데이터베이스보기를 작성하고이를 엔티티에 맵핑하는 것입니다. 위의 경우

는 JPQL 쿼리는 그런 일해야한다 :

@Query("select status, sum(totalInvoice), week from Invoice where " + 
     "left join TempTable as tt..." + <-- TEMP TABLE OR VIEW TO GET THE PERIODS 
     "issuer.id = :issuerId and type = :type and (:recipientId is null or recipient.id = :recipientId) and " + 
     "status in ('ISSUED', 'PAID') " + 
     "group by status") 

또 다른 옵션은 저장 프로 시저를 사용하는 것입니다,하지만 그들은 JPA로 구현하기 어려운 것 같다 나는 생각하지 않는다 필요하다는 것.

예상 결과 :

{ 
    "code":"xxx", 
    "title":"This is the title of the first series" 
    "type":"PERIODIC", 
    "period":"WEEKLY", <-- PERIOD 
    "from":"2015-08-02", 
    "to":"2015-08-29", 
    "labels": ["2015-08-02", "2015-08-09", "2015-08-16", "2015-08-23"], 
    "tabelType": "TEXT", 
    "series":[ 
     { 
     "code":"xxx", 
     "title":"This is the title of the first series" 
     "values":[10, 0, 13, 18] <- in this example, we don't have values for label "2015-08-09" 
     }, 
     { 
     "code":"xxx", 
     "title":"This is the title of the second series" 
     "values":[10, 0, 13, 18] <- in this example, we don't have values for label "2015-08-09" 
     } 
    ] 
} 
+1

포스트 그레스,이 일반적으로 수행된다 'SELECT ... generate_series (timestamp_from, timestamp_to, 간격)로부터 AS int_start 가입 :

나는 다음 클래스와 의미있는 무언가로이 결과를 설정 한 귀하의 데이터 _ 데이터 _tataestamp_col 사이 int_start 및 int_start + interval'] (https://www.postgresql.org/docs/current/static/functions-srf.html),하지만 JPA 친화적 인 방법으로 생각할 수 없다 (다른 네이티브 쿼리를 사용하는 것보다). – pozs

+0

@pozs, 예, JPA에서이를 수행하는 방법은 가장 어려운 부분입니다. 아마도 JPA 엔티티에 매핑 된 뷰를 사용하려고 생각하고 있었지만 실제로 작동하는지 여부는 알 수 없습니다. –

답변

2

@pozs가 답을 제공했습니다. 이것은 고유 쿼리 (PostgreSQL)를 통해서만 수행 할 수 있습니다. 결과는 다음과 같습니다.

/** 
* Returns the counts, totals and averages of the states by their currency, period and status. 
*/ 
@Query(value = "select i.currency, date(p), i.status, count(id), sum(coalesce(i.total, 0)), avg(coalesce(i.total, 0)) " + 
    "from generate_series(date_trunc(:period, cast(:from as timestamp)), date_trunc(:period, cast(:to as timestamp)) + cast('1 ' || :period as interval), cast('1 ' || :period as interval)) p " + 
    "inner join invoice i on i.due_date >= p and i.due_date < p + cast('1 ' || :period as interval) " + 
    "where issuer_id = :issuerId and type = :type and (:recipientId = 0 or recipient_id = :recipientId) and type = :type " + 
    "group by i.currency, date(p), i.status " + 
    "order by i.currency, date(p), i.status", nativeQuery = true) 
List<Object[]> getIssuerStatementTotalsByCurrencyPeriodAndStatus(
    @Param("issuerId") long issuerId, 
    @Param("recipientId") long recipientId, 
    @Param("type") String statementType, 
    @Param("from") String from, 
    @Param("to") String to, 
    @Param("period") String period); 

Object 배열 목록을 반환합니다. 또한 열거 형 및 복잡한 매개 변수를 메서드에 전달할 수 없음을 유의하십시오. 나는 문자열과 프리미티브에이 값들을 벙어리 내려야 만했다.

/** 
    * Contains the result of a single result in an aggregate query. 
    */ 
    public class AggregateResult { 

     private List<String> keys; 
     private List<BigDecimal> values; 

     @SuppressWarnings("unused") 
     public AggregateResult(Object value1, Object value2) { 
      this(new Object[] { value1, value2 }); 
     } 

     @SuppressWarnings("unused") 
     public AggregateResult(Object value1, Object value2, Object value3) { 
      this(new Object[] { value1, value2, value3 }); 
     } 

     @SuppressWarnings("unused") 
     public AggregateResult(Object value1, Object value2, Object value3, Object value4) { 
      this(new Object[] { value1, value2, value3, value4 }); 
     } 

     @SuppressWarnings("unused") 
     public AggregateResult(Object value1, Object value2, Object value3, Object value4, Object value5) { 
      this(new Object[] { value1, value2, value3, value4, value5 }); 
     } 

     public AggregateResult(Object... vals) { 
      values = new ArrayList<>(); 
      while (values.size() < vals.length && vals[vals.length - values.size() - 1] instanceof Number) { 
       Number number = (Number) vals[vals.length - values.size() - 1]; 
       values.add(number instanceof BigDecimal ? (BigDecimal) number : new BigDecimal(number.toString())); 
      } 

      this.keys = Stream.of(ArrayUtils.subarray(vals, 0, vals.length - values.size())).map(Object::toString).collect(toList()); 
     } 

     public List<String> getKeys() { 
      return keys; 
     } 

     public List<BigDecimal> getValues() { 
      return values; 
     } 

     /** 
     * Returns the list of {@link AggregateResult}s for the raw result. The raw result is expected to 
     * have been returned from a native JPA query. 
     */ 
     public static List<AggregateResult> fromNativeResult(List<Object[]> raw) { 
      return raw.stream().map(AggregateResult::new).collect(toList()); 
     } 
    } 
1

이 질문에 직접 대답 할 수 있지만하지 않을 수 있습니다 : 당신은 왜 JPA 쿼리 대신 직접 자바 코드 내에서 그룹을 어떻게해야합니까? 이러한 유형의 복잡한 의미 그룹화는 Java를 사용하여 완벽하게 수행되는 것이지만 SQL 데이터베이스는 일반적으로 이러한 유형의 구조화 된 데이터를 생성하는 데 그리 좋지 않습니다. 데이터베이스 레벨에서이 작업을 수행해야하는 다른 이유가 없다면 (예를 들어 마침표를 기준으로 검색 가능한이 데이터로 채워진 뷰가 필요함) 원시 데이터를로드하고 Java 코드를 사용하여 구조를 채 웁니다. 코딩 오버 헤드를 줄이고 아마 더 효율적 일 것입니다.

+0

답장을 보내 주셔서 감사합니다.하지만 자바 코드에서 이런 종류의 작업은 실제로 느려야한다고 생각합니다. 보통 SQL은 꽤 빠릅니다. –

+0

엄격하게 원하는 작업 유형에 따라 다릅니다. 그러나 Java는 메모리에서 작동하고 SQL 서버는 대개 캐시 된 쿼리/색인을 제외하고는 잘 프로그래밍 된 Java 코드가 SQL에 비해 빠릅니다. 그러나 다양한 유형의 임시 그룹화에서 Java는 대부분의 경우 SQL보다 빠를 것입니다. 또한 코드 명확성 및 관리 효율성에 대한 숨겨진 비용에 대해서도 기억해야합니다. 데이터베이스 솔루션이 더 빠르다고 할지라도 문제는 읽기 어렵고 유지 관리가 어려운 솔루션에서 가격을 지불할지 여부입니다. –

관련 문제