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概念正是允许这样做的。

4

2 回答 2

2

AFAIK,您不能在 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;
于 2011-03-16T16:28:28.800 回答
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 下的条目数。呸。

于 2011-03-24T13:17:58.293 回答