ハマログ

株式会社イーツー・インフォの社員ブログ

集計処理の実装

大分間が空いてしまいましたが、前回の
キーブレークを使った集計
の実装を行ってみます

前々回のhibernateで単純なSELECT文の順読み込み
のAppクラスに変更を加えました

実装の主な要点は

  • ブレークキーや集計項目の割当の場所
  • 最後の1件の出力を忘れないこと

です

下が実装です

App.java

package package com.example.exp1;

import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 * Hello world!
 *
 */
public class App 
{
    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static void main( String[] args )
    {
        SessionFactory f = buildSessionFactory();
        Session ss = f.openSession();
        ss.beginTransaction();
        ScrollableResults rs = ss.createSQLQuery("select * from URIAGE_CHOU order by NENGETSU")
                .addEntity(UriageChou.class)
                .scroll(ScrollMode.FORWARD_ONLY)
                ;
        
        // 読み込んだレコード件数。ループ後の最後の1件の出力で使います
        int recordCount = 0;
        // 集計用変数
        String nengetsuSum = null;
        int gakuSum = 0;
        while(rs.next()) {
            ++recordCount;
            UriageChou u = (UriageChou) rs.get(0);
            // ブレークしたら集計結果を出力します
            if(breaks(u.getNengetsu(), nengetsuSum)) {
                // 集計結果を出力
                System.out.println(nengetsuSum + ", " + Integer.toString(gakuSum));
                // 集計用変数を初期化
                gakuSum = 0;
            }
            nengetsuSum = u.getNengetsu();
            gakuSum += u.getGaku().intValue();
        }
        // ループする対象のレコードが無くなり、集計用の変数にの残っている最後の集計結果を出力する
        if(recordCount > 0) {
            // 集計結果を出力
            System.out.println(nengetsuSum + ", " + Integer.toString(gakuSum));
        }
        if(recordCount == 0) {
            System.out.println("集計対象のレコードがありませんでした");
        }
                
        ss.getTransaction().commit();
    }
    
    /**
     * ブレークしたかを判定します
     * @param nengetsuComing 読み込んだレコードのブレークキー
     * @param nengetsuSum 集計中のブレークキー
     * @return trueならブレークしている
     */
    public static boolean breaks(String nengetsuComing, String nengetsuSum) {
        if(nengetsuComing == null) {
            throw new IllegalArgumentException();
        }
        // 1レコード目をブレークと見なさないために、
        // nengetsuSumがnullの場合は、ブレークでないものとする
        if(nengetsuSum == null) {
            return false;
        }
        boolean result = false == nengetsuSum.equals(nengetsuComing);
        return result;
    }
    
}

実行結果

Hibernate: select * from URIAGE_CHOU order by NENGETSU
201305, 100
201306, 130

このような集計処理の、何が嬉しいのか

このようキーブレークによる集計処理が、どんな場面で有用なのかを考えてみます

全ての集計結果を保持すると、大量のメモリを消費する場面

下の参考で言及されているMapを使った実装方法では
全ての集計結果を、集計が終わるまで、メモリに持ちます

キーブレークを使った実装では、集計ができるごとに、出力することができます
そのときにファイルやデータベースへ保存すれば、集計結果をメモリに持つ必要がありません

例えばもし、全ての集計結果をメモリへ持ったときの、
メモリ使用量が、100MBとか大量のメモリを消費するような
状況が起こったします
そうすると、その集計処理や、同時に実行されている他の処理が、
不安定になるようなことも、あるでしょう

集計が複雑な場面

この記事の例に上げた集計では、SQL文で書けるようなものです
現実世界の集計では、

  • 集計元が、幾つにも分かれている
  • 集計条件が、SQL文で書けないくらい複雑

というような状況があります

おわりに

今回は実務で、キーブレークによる集計処理が、
有用な場面に自分が遭遇したので、記事にしようと、思い立ちました
何かの参考になればと思います

参考させて頂いた記事

下の記事を参考にさせて頂きました
ありがとうございます

キー毎に値を集計する方法

http://blog.goo.ne.jp/hishidama/e/0c0b4097643ad7d4f737cca79731587c

リストを項目ごとに集計する

http://d.hatena.ne.jp/irof/20111203/p1

  桑原 光昭   2014年3月24日


関連記事

PlantUML&AtomでらくらくUML

ドキュメントが嫌いです。男はコードで殴り合うべきだと思うのですがどうでしょうか?…

Laravel4でForm::checkboxのチェック状態判定

laravel4にてForm::checkboxのチェック状態判定を行いました。…

Laravel5.1のリリース

Laravel5.1がリリースされました。PSR-2の採用、イベントブロードキャ…


← 前の投稿

次の投稿 →