Firebird Konstante UDF
-
25-10-2019 - |
Frage
Ist es möglich, eine externe Funktion als konstant oder unveränderlich zu machen, damit Firebird sie im Verlauf einer SQL -Anweisung nicht neu berechnen?
Im folgenden Beispiel (Firebird 2.1) möchte ich GetTimeofday () wie Current_Timestamp verhalten, aber es wird stattdessen zweimal bewertet:
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
Wie Sie sehen können, bleibt der Wert von "Ts" konstant, aber meine "time_t" ruft über die Aufrufe von FB_SEEP () vor. (FB_SEEP ist eine Komfortfunktion für die angegebene Anzahl von Sekunden.)
Ist was ich möglich will? Ich weiß, dass PostgreSQL dies genau mit seinem Konzept von erlaubt STABIL Funktionen.
Lösung 2
In Kürze
Die kurze Antwort lautet "Nein", und die dazugehörige Anleitung lautet: "Führen Sie Ihren Server immer in UTC aus."
Problemumgehungen
Einfachster Fall: Stabile UTC -Zeitstempel
(Dies war mein ursprüngliches Ziel.) Wenn current_timestamp genug ist, führen Sie einfach Ihren Server in UTC aus. Kein UDF erforderlich.Explizit vorkomputieren UDF
Es gibt keinen direkt unterstützten Weg, um einen UDF zu "stabilisieren". Die meisten Kunden sind also besser dran, den Rückgabewert des UDF vorzubereiten, wodurch dieser wörtliche Wert als von Kunden gelieferter Parameter in einem GTT usw. verfügbar ist.
Kludge
Current_transaction und current_timestamp identifizieren Sie eine individuelle Abfrage zumindest zur Genauigkeit von Current_Timestamp. (Angenommen, die Uhr befindet sich in UTC, damit sich die Zeit bei einer Tageslichtveränderung nicht wiederholt.)
In diesem Sinne könnte eine ausgewählbare gespeicherte Prozedur den Rückgabewert des UDF zwischenspeichern als Zeichenfolge Verwenden Sie RDB $ set_context und rdb $ get_context, speichern im user_transaction -Kontext und aus dem Current_Timestamp. Fügen Sie eine kleine zusätzliche Logik hinzu, um die Anzahl der unter user_transaction gespeicherten Einträge zu beschneiden. Yuck.
Andere Tipps
Afaik, Sie können einen UDF nicht als konstant oder inmutierbar in Firebird markieren, aber als Problemumgehung können Sie sich auf eine Inline-Ansicht (auch bekannt als Tabelle) verlassen, um das zu erreichen, was Sie wollen: Wählen Sie den Wert nur einmal aus und verwenden Sie ihn als Konstante in Ihren Ergebnissen. Ich habe keine UDF zur Hand, um einen Test zu machen. Vielleicht einen Syntaxfehler, aber ich hoffe, Sie sehen die Idee dahinter:
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;
Sie können sich auch auf eine ausgewählbare gespeicherte Prozedur verlassen, um die UDF einmal auszuführen und die Spalte zu den Ergebnissen einer bestimmten Abfrage hinzuzufügen
Bearbeiten Wie angefordert, schließe ich das gespeicherte Verfahren ein:
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;