Firebird常量UDF

时间:2011-03-16 13:05:32

标签: sql firebird immutability firebird2.1

是否可以将EXTERNAL FUNCTION设为常量或不可变,以便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正是以STABLE FUNCTIONS。

的概念准​​确地允许这一点

2 个答案:

答案 0 :(得分:2)

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;

答案 1 :(得分:1)

简介

简短的回答是“不”,随附的指导是“始终以UTC格式运行您的服务器。”

<强>变通方法

  • 最简单的情况:稳定的UTC时间戳
    (这是我最初的目标。)如果CURRENT_TIMESTAMP足够精确,只需以UTC格式运行服务器即可。不需要UDF。

  • 明确预先计算UDF
    没有直接支持的方法来“稳定”UDF。因此,大多数客户最好只是预先计算UDF的返回值,在GTT等中将该文字值作为客户端提供的参数提供。

<强>未完善

CURRENT_TRANSACTION和CURRENT_TIMESTAMP一起有效地识别单个查询,至少达到CURRENT_TIMESTAMP的精度。 (再次假设时钟是UTC,以免在夏令时发生变化时自行重复。)

考虑到这一点,可选择的存储过程可以使用RDB $ SET_CONTEXT和RDB $ GET_CONTEXT将UDF的返回值缓存为字符串,存储在USER_TRANSACTION上下文中并键入CURRENT_TIMESTAMP。添加一些额外的逻辑来修剪USER_TRANSACTION下存储的条目数。呸。