kelompok kriteria jpa berdasarkan nilai agregat

Saya memerlukan kueri untuk mengubah waktu (milidetik) menjadi distribusi per jam.

meja saya memiliki kolom bernama stempel waktu: nilai dalam milidetik. untuk mengelompokkan berdasarkan jam, saya perlu melakukan pembagian berikut dengan 1000 -> detik dibagi dengan 3600 -> jam -> mod 24 -> dapatkan jam pada hari catatan dibuat.

stempel waktunya panjang:

    public static volatile SingularAttribute<VersionJpa, Long> timestamp;

Saya kemudian dapat menampilkan hasilnya dalam distribusi harian. Namun, ini tidak berhasil, grup tidak berfungsi dan saya mendapatkan daftar lengkapnya tidak dikelompokkan dan tidak dihitung.

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Tuple> cq = cb.createTupleQuery();
    Root<VersionJpa> root = cq.from(VersionJpa.class);
    cq.where(p);
    Expression<Integer> group = cb.mod(cb.toInteger(cb.quot(root.get(VersionJpa_.timestamp), 3600000)),24);
    cq.multiselect(group, cb.count(root));
    cq.groupBy(group);
    TypedQuery<Tuple> tq = em.createQuery(cq);
    return tq.getResultList();

SQL (mysql) ini menghasilkan hasil yang benar. apakah ada batas bawah yang setara di JPA? Mencoba sebagai.(Bilangan Bulat) || ke bilangan bulat()

    select mod(floor(stamp/3600000), 24) , count(*) from versions group by mod(floor(stamp/3600000), 24);

Saya menemukan bahwa selama pengujian unit (di Derby) ini berjalan dengan baik, tetapi dalam lebih banyak produksi seperti pengaturan (MySQL) ia tidak memasukkan agregat paling dalam ke bilangan bulat, sehingga grup dilakukan pada nilai mengambang dan bukan pada 24 (0 , 1, .. 23) nilai potensial (return ribuan kelompok).

    Expression<Integer> hours = cb.mod(cb.quot(root.get(VersionJpa_.timestamp).as(Integer.class), 3600000).as(Integer.class),24).as(Integer.class);
    cq.multiselect(hours, cb.count(root));
    cq.groupBy(hours);

menambahkan dukungan dan pengujian PostgreSQL menyebabkan kesalahan lain yang membuat saya berpikir bahwa kueri saya salah:

    RuntimeException Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: org.postgresql.util.PSQLException: ERROR: column "versions.stamp" must appear in the GROUP BY clause or be used in an aggregate function Position: 13 Error Code: 0 Call: SELECT MOD((STAMP / ?), ?), COUNT(ID) FROM VERSIONS GROUP BY MOD((STAMP / ?), ?) LIMIT ? OFFSET ? bind => [6 parameters bound] Query: TupleQuery(referenceClass=VersionJpa sql="SELECT MOD((STAMP / ?), ?), COUNT(ID) FROM VERSIONS GROUP BY MOD((STAMP / ?), ?) LIMIT ? OFFSET ?")

bantuan apa pun dihargai.


person Gadi    schedule 02.08.2013    source sumber


Jawaban (2)


Anda dapat menggunakan metode function() untuk memanggil fungsi database apa pun.

Lihat, http://java-persistensi-kinerja.blogspot.com/2012/05/jpql-vs-sql-have-both-with-eclipselink.html

Postgres sepertinya lebih banyak mengeluh tentang penggunaan fungsi di grup oleh, di JPQL Anda bisa mencoba,

Select mod((v.timestamp / 3600000), 24) h, count(v) from Version v group by h
person James    schedule 07.08.2013
comment
terima kasih, saya sudah mencobanya dengan lantai dan tidak berhasil. Saya perlu bekerja dengan kriteria karena saya menambahkan lebih banyak pemfilteran/pemilihan secara lebih dinamis. apakah ini bug gerhana atau saya hanya menggunakan sintaks yang salah? - person Gadi; 08.08.2013

bagi siapa pun yang pernah mengalami masalah ini, saya menemukan solusi berdasarkan kolom database dan bukan pertanyaan rumit. sebagai pengingat, masalahnya adalah dukungan vendor yang berbeda tidak dijamin. ini berfungsi di Derby tetapi gagal di MySQL.

Saya membuat kolom baru untuk setiap metrik yang saya minati:

  • tahun
  • bulan
  • hari dalam seminggu
  • hari dalam sebulan
  • jam-hari (24)

lalu mengisinya dengan kalender yang diinisialisasi dengan pengaturan lokal dan waktu sistem.

    Calendar now = Calendar.getInstance();
    now.setTimeInMillis(timestamp);
    this.year = now.get(Calendar.YEAR);
    logger.trace("Year : [{}]", year);
    this.month = now.get(Calendar.MONTH);
    logger.trace("Month : [{}]", month);
    this.weekOfYear = now.get(Calendar.WEEK_OF_YEAR);
    logger.trace("Week of Year : [{}]", weekOfYear);
    this.dayOfMonth = now.get(Calendar.DAY_OF_MONTH);
    logger.trace("Day of Month : [{}]", dayOfMonth);
    this.dayOfWeek = now.get(Calendar.DAY_OF_WEEK);
    logger.trace("Day of Week : [{}]", dayOfWeek);
    this.hourOfDay = now.get(Calendar.HOUR_OF_DAY);
    logger.trace("Hour of Day : [{}]", hourOfDay);

semoga ini bisa membantu seseorang. bersulang.

person Gadi    schedule 09.08.2013