Firebird Constant UDF
-
25-10-2019 - |
質問
Firebirdが1つのSQLステートメントの過程でそれを再計算しないことを知っているように、外部関数を一定または不変として作成することは可能ですか?
以下の例(Firebird 2.1)では、gettimeofday()がcurrent_timestampのように振る舞いたいと思いますが、代わりに2回評価されます。
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を1回実行し、特定のクエリの結果に列を追加することもできます
編集 要求に応じて、私はストアドプロシージャを含めています:
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;