Firebird постоянная UDF
Можно ли сделать ВНЕШНУЮ ФУНКЦИЮ постоянной или неизменной, чтобы Firebird знал, что не нужно пересчитывать ее в течение одного оператора SQL?
В приведенном ниже примере (Firebird 2.1) я бы хотел, чтобы GETTIMEOFDAY() вел себя как CURRENT_TIMESTAMP, но вместо этого он оценивается дважды:
SQL> DECLARE EXTERNAL FUNCTION gettimeofday -- gettimeofday(2) wrapper
CON> RETURNS DOUBLE PRECISION BY VALUE
CON> ENTRY_POINT 'UDF_gettimeofday' MODULE_NAME 'udf_gettimeofday';
SQL> SELECT CURRENT_TIMESTAMP AS ts,
CON> CAST(GETTIMEOFDAY() AS INTEGER) AS time_t,
CON> FB_SLEEP(2) AS zzz
CON> FROM rdb$database
CON> CROSS JOIN (SELECT 1 AS foo
CON> FROM rdb$database
CON> UNION ALL
CON> SELECT 2
CON> FROM rdb$database) d;
TS TIME_T ZZZ
========================= ============ ============
2011-03-15 20:57:46.0390 1300244268 0
2011-03-15 20:57:46.0390 1300244270 0
Как видите, значение "TS" остается постоянным, но мой "TIME_T" продвигается через вызовы FB_SLEEP(). (FB_SLEEP - это удобная функция для приостановки на указанное количество секунд.)
Возможно ли то, что я хочу? Я знаю, что PostgreSQL разрешает именно это благодаря своей концепции СТАБИЛЬНЫХ ФУНКЦИЙ.
2 ответа
Вкратце
Краткий ответ "нет", и сопровождающее руководство "всегда запускайте свой сервер в UTC".
обходные
Простейший случай: стабильные метки времени UTC
(Это было моей первоначальной целью.) Если CURRENT_TIMESTAMP достаточно точен, просто запустите свой сервер в формате UTC. UDF не требуется.Явно предварительно вычислить UDF
Нет прямого способа "стабилизировать" UDF. Таким образом, большинству клиентов лучше просто предварительно вычислить возвращаемое значение UDF, сделав это буквальное значение доступным в качестве параметра, предоставленного клиентом, в GTT и т. Д.
клюдж
CURRENT_TRANSACTION и CURRENT_TIMESTAMP, взятые вместе, эффективно идентифицируют отдельный запрос, по крайней мере, с точностью до CURRENT_TIMESTAMP. (Снова предполагая, что часы находятся в UTC, чтобы время не повторилось во время изменения летнего времени.)
Это означает, что выбираемая хранимая процедура может кэшировать возвращаемое значение UDF в виде строки, используя RDB$SET_CONTEXT и RDB$GET_CONTEXT, сохраняя в контексте USER_TRANSACTION и отключая CURRENT_TIMESTAMP. Добавьте немного дополнительной логики, чтобы также сократить количество записей, хранящихся в USER_TRANSACTION. Тьфу.
AFAIK, вы не можете пометить UDF как постоянную или неизменную в Firebird, но в качестве обходного пути вы можете полагаться на встроенное представление (или производную таблицу), чтобы достичь того, что вы хотите: выберите значение только один раз и используйте его как константу в ваших результатах. У меня нет UDF под рукой, чтобы сделать тест, так что, возможно, какая-то синтаксическая ошибка, но я надеюсь, что вы поймете эту идею:
SELECT CURRENT_TIMESTAMP AS ts,
q1.time_t,
FB_SLEEP(2) AS zzz
FROM rdb$database
CROSS JOIN (select CAST(GETTIMEOFDAY() AS INTEGER) AS time_t from rdb$database)
CROSS JOIN (SELECT 1 AS foo
FROM rdb$database
UNION ALL
SELECT 2
FROM rdb$database) d;
Вы также можете положиться на выбираемую хранимую процедуру для однократного запуска udf и добавления столбца к результатам данного запроса.
Редактировать В соответствии с просьбой, я включаю хранимую процедуру:
SET TERM ^ ;
CREATE PROCEDURE ExampleSP
RETURNS
(
ts timestamp
, time_t integer
, zzz integer
)
as
BEGIN
SELECT CAST(GetTimeOfDay() AS Integer)
FROM rdb$database
INTO :time_t;
for SELECT Current_Timestamp AS ts,
FB_SLEEP(2) AS zzz
FROM rdb$database
CROSS JOIN (SELECT 1 AS foo
FROM rdb$database
UNION ALL
SELECT 2
FROM rdb$database) d
INTO :ts, :zzz do
SUSPEND;
END
^
SET TERM ; ^
SELECT * FROM ExampleSP;