Как перевести миллис на дату и группировать по месяцам и годам в Jooq? - PullRequest
1 голос
/ 24 марта 2020

У меня есть следующее SQL, которое работает как ожидалось:

select  YEAR(CONVERT_TZ(FROM_UNIXTIME(creation_date / 1000), @@session.time_zone, "Europe/Berlin")) as year,
    MONTH(CONVERT_TZ(FROM_UNIXTIME(creation_date / 1000), @@session.time_zone, "Europe/Berlin")) as month,
    sum(value)
from transaction
GROUP BY year, month; 

Я пытаюсь воссоздать эти SQL в Jooq, но я не знаю, как создать объект Date из миллисекунды в моей базе данных creation_date.

dsl.select(DSL.month(DSL.date(TRANSACTION.CREATION_DATE)), // This does not work
           DSL.year(DSL.date(TRANSACTION.CREATION_DATE)), // This does not work
           DSL.sum(TRANSACTION.VALUE))
       .from(TRANSACTION)
       .groupBy(???); // How to group by month and year?

1 Ответ

3 голосов
/ 25 марта 2020

Распространенная путаница при записи SQL GROUP BY - логический порядок SQL операций . Хотя синтаксически SELECT кажется раньше GROUP BY, логически порядок обратный. Это означает, что вы не можете ссылаться на столбцы в предложении SELECT из предложения GROUP BY.

В некоторых диалектах могут быть реализованы исключения «для удобства», но это, как правило, очень запутанно. Я рекомендую этого не делать.

Но для решения вашей проблемы:

Создание SQL, которое вы хотели произвести

В то время как в исходном запросе SQL вы использовали псевдонимы для ваших двух выражений (AS year и AS month), в запросе jOOQ вы этого не сделали. Я также рекомендую использовать псевдонимы и назначать выражения столбцов локальным переменным для повторного использования в предложении groupBy():

Field<?> month = DSL.month(DSL.date(TRANSACTION.CREATION_DATE)).as("month");
Field<?> year  = DSL.year(DSL.date(TRANSACTION.CREATION_DATE)).as("year");

dsl.select(month, year, DSL.sum(TRANSACTION.VALUE))
   .from(TRANSACTION)
   .groupBy(month, year)
   .fetch();

Столбцы с псевдонимами создают полное объявление в предложении SELECT, но только псевдоним во всех других пунктах, так что это делает именно то, что вы хотели:

SELECT
  month(date(transaction.creation_date)) as month,
  year(date(transaction.creation_date)) as year
  sum(transaction.value)
FROM transaction
GROUP BY
  month,
  year;

Лучше SQL оператор в соответствии с логическим порядком операций

Если вы хотите, чтобы ваш SQL оставайтесь переносимыми и корректными в соответствии с логическим порядком операций SQL, которые я упомянул, я рекомендую написать это вместо:

Field<?> month = DSL.month(DSL.date(TRANSACTION.CREATION_DATE));
Field<?> year  = DSL.year(DSL.date(TRANSACTION.CREATION_DATE));

dsl.select(month.as("month"), year.as("year"), DSL.sum(TRANSACTION.VALUE))
   .from(TRANSACTION)
   .groupBy(month, year)
   .fetch();

Обратите внимание, что я перенес псевдоним в предложение SELECT, тогда как в предложении GROUP BY я сейчас ссылаюсь на полные выражения столбцов. Это приведет к следующему запросу:

SELECT
  month(date(transaction.creation_date)) as month,
  year(date(transaction.creation_date)) as year
  sum(transaction.value)
FROM transaction
GROUP BY
  month(date(transaction.creation_date)),
  year(date(transaction.creation_date));

Полные выражения теперь развернуты в предложение GROUP BY, без необходимости повторять их вручную.

...