Дана такая таблица:
CREATE TABLE demo (
id BIGINT PRIMARY KEY,
offset_minutes INTEGER NOT NULL,
scheduled_time TIMESTAMP WITH TIME ZONE
);
Я хочу обновить scheduled_time
до указанного в приложении времени плюс offset_minutes
. В простом SQL на PostgreSQL это будет выглядеть примерно так
UPDATE demo
SET scheduled_time =
timestamp with time zone '2022-09-12T01:23:45Z' +
offset_minutes * interval '1 minute'
WHERE id = 12345;
Как лучше всего выразить это в jOOQ независимым от СУБД способом?
Это противоположность обычному способу, которым люди хотят добавить минуты к значению времени, что хорошо освещено другими вопросами SO: в моем случае Instant
предоставляется кодом, а количество минут находится в столбце базы данных, А не наоборот.
Лучшее, что мне удалось придумать, это вычислить количество минут с плавающей запятой, поскольку мы можем добавлять дни к Instant
s:
dslContext.update(DEMO)
.set(DEMO.SCHEDULED_TIME,
DSL.instant(instant).add(DEMO.OFFSET_MINUTES.div(24.0 * 60.0)))
.where(DEMO.ID.eq(id))
.execute();
В PostgreSQL jOOQ генерирует выражение SQL для значения предложения SET
:
(timestamp with time zone '2022-09-12 01:23:45+00:00' +
("public"."demo"."offset_minutes" / 1.44E3) * interval '1 day')
Тот факт, что jOOQ генерирует interval '1 day'
, вселяет в меня надежду, что есть способ заставить его изменить day
на minute
и избежать вычислений с плавающей запятой. Я никогда не схожу с ума от выполнения вычислений с плавающей запятой для дискретных величин, если этого можно избежать.
Решение вашей непосредственной проблемы
Ожидается запрос функции №6723 на добавление поддержки DSL::offsetDateTimeAdd
, что позволит добавлять интервалы к TIMESTAMP WITH TIME ZONE
типам данных.
Это по-прежнему не будет работать для Instant
типов данных, где нам потребуется еще одна «перегрузка» для всех возможных вариантов арифметики даты и времени.
Оба они будут создавать множество новых методов в уже переполненном классе DSL
, не добавляя много новой функциональности, учитывая, что они делают что-то похожее на существующие функции timestampAdd()
или localDateTimeAdd()
, просто предлагая то же самое для новых типов.
Более стратегическое, основательное изменение
В долгосрочной дорожной карте предлагается большое изменение №11088, которое позволит более четко разделить два обычных типа T
и U
, связанные с Field
:
T
является «типом JDBC», то есть типом, который понимает база данных (например, OffsetDateTime
)U
является «типом пользователя», то есть типом, который вы хотите видеть в своем коде (например, Instant
)Таким образом, будет единственный метод, принимающий T = OffsetDateTime
и любой произвольный тип U
(например, Instant
).
Хотя выполнение вышеуказанных задач требует времени, вы всегда можете воспользоваться обычным аварийным люком и использовать простые шаблоны SQL. Например.
DSL.field("({0} + {1} * interval '1 minute')",
SQLDataType.INSTANT,
DSL.instant(instant),
DEMO.OFFSET_MINUTES
);
Вы можете извлечь жестко закодированные аргументы (DSL.instant(instant)
и DEMO.OFFSET_MINUTES
) и создать повторно используемую функцию для вышеперечисленного и, таким образом, создать мини-библиотеку для недостающей функциональности в jOOQ.