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.

War es hilfreich?

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;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top