Как извлекать группы, упорядоченные по агрегативному значению, с помощью jooq

#java #group-by #grouping #aggregate-functions #jooq

#java #группировать по #группировка #агрегатные функции #jooq

Вопрос:

Я хочу реализовать этот запрос с помощью jooq:

 SELECT bank_id, array_agg(id)
FROM aggregative_line_item
GROUP BY bank_id
ORDER BY sum(amount) desc;
 

Теперь jooq предоставляет функцию fetchGroups(), которая идеально сопоставляет значения с картой<Строка, список>

  create.selectFrom(aggregative_line_item)
            .fetchGroups(aggregative_line_item.bank_id, aggregative_line_item.id);
 

Однако я не понимаю, как упорядочить возвращаемые результаты по значению sum().

Ответ №1:

Когда вы используете ResultQuery.fetchGroups() , вы группируете результирующие наборы в Java, а не в SQL. Для этого есть много законных случаев, но в вашем случае, я думаю, вы должны делать все в SQL напрямую, предполагая, что вы используете PostgreSQL, потому что вы уже использовали ARRAY_AGG() в своем SQL-запросе. Просто напишите:

 Result<Record2<String, Integer[]>> result =
create.select(AGGREGATIVE_LINE_ITEM.BANK_ID, arrayAgg(AGGREGATIVE_LINE_ITEM.ID))
      .from(AGGREGATIVE_LINE_ITEM)
      .groupBy(AGGREGATIVE_LINE_ITEM.BANK_ID)
      .orderBy(sum(AGGREGATIVE_LINE_ITEM.AMOUNT).desc())
      .fetch();
 

Это предполагает обычный статический импорт, в том числе:

 import static org.jooq.impl.DSL.*;
 

Результат не совсем в той форме, которую вы искали, но вы все равно можете вызвать fetchMap() результат, например

 Map<String, Integer[]> map = result.fetchMap(
  AGGREGATIVE_LINE_ITEM.BANK_ID, 
  arrayAgg(AGGREGATIVE_LINE_ITEM.ID)
);
 

Или, вместо повторения выражений, вы можете присвоить их локальным переменным и повторно использовать их:

 Field<String> key = AGGREGATIVE_LINE_ITEM.BANK_ID;
Field<Integer[]> value = arrayAgg(AGGREGATIVE_LINE_ITEM.ID);

// And then:
Map<String, Integer[]> map = 
create.select(key, value)
      .from(AGGREGATIVE_LINE_ITEM)
      .groupBy(AGGREGATIVE_LINE_ITEM.BANK_ID)
      .orderBy(sum(AGGREGATIVE_LINE_ITEM.AMOUNT).desc())
      .fetchMap(key, value);
 

Тем не менее, если вы предпочитаете List<Integer> тип над Integer[] типом, вы могли Collector бы вместо этого использовать API-интерфейсы JDK ResultQuery.collect() :

 Map<String, List<Integer>> map = 
create.select(AGGREGATIVE_LINE_ITEM.BANK_ID, arrayAgg(AGGREGATIVE_LINE_ITEM.ID))
      .from(AGGREGATIVE_LINE_ITEM)
      .groupBy(AGGREGATIVE_LINE_ITEM.BANK_ID)
      .orderBy(sum(AGGREGATIVE_LINE_ITEM.AMOUNT).desc())
      .collect(Collectors.toMap(
         r -> r.value1(),
         r -> Arrays.asList(r.value2())
      ));
 

Комментарии:

1. Стоит отметить, что результат #intoGroup() в настоящее время реализован с использованием LinkedHashMap, который гарантировал сохранение первоначального порядка (конечно, это недокументированная деталь реализации, которая может измениться)

2. @AmitGoldstein: имеет смысл. Это задокументировано в некоторых местах, но не в других. Хотя мы не будем гарантировать LinkedHashMap реализацию, мы, безусловно, можем гарантировать стабильность порядка итераций (что LinkedHashMap и происходит). Мы улучшим это в ближайшем будущем: github.com/jOOQ/jOOQ/issues/11154