Работает ли агрегатная функция JPQL avg с целыми числами? - PullRequest
5 голосов
/ 08 мая 2010

У меня есть объект JPA 2 с именем Хирургия . У него есть член с именем transfusionUnits , то есть Integer .

В базе данных есть две записи. Выполнение этого оператора JPQL:

Select s.transfusionUnits from Surgery s

дает ожидаемый результат:

2
3

Следующее утверждение дает ожидаемый ответ 5 :

Select sum(s.transfusionUnits) from Surgery s

Я ожидаю, что ответом следующего утверждения будет 2.5 , но вместо этого он возвращает 2.0 .

Select avg(s.transfusionUnits) from Surgery s

Если я выполню инструкцию для другого (Float) члена, результат будет правильным. Любые идеи о том, почему это происходит? Нужно ли делать какие-то приведения в JPQL? Это вообще возможно? Конечно, мне здесь не хватает чего-то тривиального.

Ответы [ 2 ]

9 голосов
/ 08 мая 2010

Во-первых, позвольте мне обобщить, что спецификация JPA 2.0 говорит об AVG (см. Раздел 4.8.5 Агрегатные функции в предложении SELECT для полного содержимого)

Функция AVG принимает в качестве аргумента выражение пути поля состояния и вычисляет среднее значение поля состояния по группе. Поле состояния должно быть числовым, а результат возвращается в виде Double.

Итак, после первого чтения я ожидал, что следующий фрагмент пройдет:

Query q = em.createQuery("select avg(s.transfusionUnits) from Surgery s");
Double actual = (Double) q.getSingleResult();
assertEquals(2.5d, actual.doubleValue());

Но это не так, я получил 2.0 как результат (удвоение, но не ожидаемый результат).

Итак, я посмотрел на уровень базы данных и понял, что SQL-запрос на самом деле не возвращает 2.5. И действительно, так сказано в документации моей базы данных об AVG:

Среднее (среднее) значение. Если ни одна строка не выбрана, результатом будет NULL. Агрегаты разрешены только в избранных утверждениях. Возвращаемое значение того же типа, что и параметр .

Я ожидал слишком многого. JPA вернет результат как Double, но он не будет творить магию. Если запрос не возвращает результат с требуемой точностью, вы не получите его на уровне Java.

Поэтому, не меняя тип transfusionUnits, мне пришлось выполнить этот собственный запрос, чтобы все заработало:

Query q = em.createNativeQuery("SELECT AVG(CAST(s.transfusionUnits as double)) from Surgery s");
Double actual = (Double) q.getSingleResult();
assertEquals(2.5d, actual.doubleValue());

Обновление: Похоже, что некоторые базы данных (например, MySQL) возвращают значение с десятичной частью при использовании AVG для столбца INT (что имеет смысл, независимо от задокументированного поведения базы данных, которую я использовал Вот). Для других баз данных приведенное выше приведение может быть обработано в «диалекте» (например, см. HHH-5173 для Hibernate), чтобы избежать проблем переносимости между базами данных при использовании JPQL.

1 голос
/ 09 февраля 2011

Приведение усредненного поля к удвоению делает свое дело. Не уверен насчет производительности, но не должно быть ничего плохого:

Select avg(s.transfusionUnits + 0.0) from Surgery s
...