是否可以使外部功能成为恒定或不可变的功能,以便在一个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;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top