2016-08-09 1 views
0

私は以下の予想される結果を生成する必要があります。基本的には、特定の期間(週ごと、月ごとなど)に基づいて値を集計するクエリです。開始日と終了日を持つ日付フィルタがあり、すべての範囲の値を返す必要があります。存在しない場合は、0を返す必要があります。JPA期間クエリを処理する正しい方法は何ですか?

次の例では、開始日は '2015-08-02'と日付 '2015-08-23'で、期間は週のみです。 2週目には値はありませんが、値がゼロで返されることに注意してください。

この場合、JPAを使用してこれを実行する最良の方法は何ですか?私たちは一時テーブルを使用してこのテーブルで結果を結合して範囲全体の結果を得ることを考えていますが、JPAを使用して可能かどうかは分かりません。なぜなら、テーブルを作成し、

もう1つの方法として、データベースビューを作成してエンティティにマップする方法があります。上記の例で

、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、間隔)などからint_start登録しようと行われていますあなたの_data.timestamp_col間のint_startとint_start +間隔 '](https://www.postgresql.org/docs/current/static/functions-srf.html)のあなたの_data.timestamp_colで、私はそれの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); 

これは、オブジェクト配列のリストを返します。また、メソッドに列挙型や複雑なパラメータを渡すことができなかったことにも注意してください。私はこれらの値を文字列とプリミティブに変更する必要がありました。私は次のクラスと意味のあるものに、この結果になってきました

:Postgresので

/** 
    * 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コードの中にグループ化を行うために必要なのですか?このタイプの複雑なセマンティックグループは、Javaを使用して完全にうまくいくものですが、SQLデータベースは一般に、このタイプの構造化データを作成するのにはあまり適していません。データベースレベルでこれを行う他の理由がない場合(たとえば、ピリオドに基づいて検索可能なこのデータで設定されたビューが必要な場合)は、生データをロードしてJavaコードを使用して構造を移入するだけです。おそらくより効率的になるでしょう。

+0

あなたの答えをありがとうが、私は、Javaコードのこの種のものは、実際には遅くなるべきだと思う。通常、SQLは非常に高速です。 –

+0

厳密には、実行したい操作のタイプに依存します。しかし、Javaはメモリー内で動作し、SQLサーバーは通常キャッシュされた照会/索引とは別のものであるため、よくプログラミングされたJavaコードは一般にSQL同等より高速です。 しかし、さまざまなタイプのアドホックグループについては、Javaはほとんどの場合、SQLより高速です。また、コードの明快さと管理容易性の隠れたコストについて覚えておく必要があります。データベースソリューションがより高速であっても、読みにくく維持しやすいソリューションで価格を支払うかどうかという問題があります。 –

関連する問題