-
25-10-2019 - |
题
是否可以使外部功能成为恒定或不可变的功能,以便在一个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等中使用。
kludge
Current_transaction和Current_timestamp一起有效地确定了一个单独的查询,至少是Current_timestamp的精度。 (再次假定时钟位于UTC中,以免在储蓄日期内重复自己的时间。)
注意,可选的存储过程可以缓存UDF的返回值 作为字符串 使用rdb $ set_context和rdb $ get_context,存储在user_transaction上下文中,并关闭Current_timestamp。添加一些额外的逻辑,以修剪user_transaction下存储的条目数量。 y
其他提示
afaik,您不能将UDF标记为Firebird中的UDF,或者在Firebird中无法将UDF标记,但是作为解决方法,您可以依靠在线视图(又称派生的表)来实现您想要的东西:仅选择一次值并将其用作常数在您的结果中。我手头没有任何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;