Question

Est-il possible de faire une fonction externe comme constante ou immuable, de sorte que Firebird ne sait pas à elle recalcule au cours d'une instruction SQL?

Dans l'exemple ci-dessous (Firebird 2.1), je voudrais gettimeofday () à se comporter comme CURRENT_TIMESTAMP, mais il est plutôt évalué deux fois:

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

Comme vous pouvez le voir, la valeur de « TS » reste constante, mais mes avances « time_t » à travers les appels FB_SLEEP (). (FB_SLEEP est une fonction de la commodité pour faire une pause pour le nombre donné de secondes).

est-ce que je veux possible? Je sais que PostgreSQL permet précisément cela avec son concept de STABLE FONCTIONS .

Était-ce utile?

La solution 2

En bref

La réponse est « non », et la direction d'accompagnement est « toujours exécuter votre serveur en UTC. »

Contournements

  • cas le plus simple: stable UTC horodatages (Ce fut mon objectif initial.) Si CURRENT_TIMESTAMP est assez précis, il suffit de lancer votre serveur en UTC. Aucune UDF requise.

  • Explicitement Précalculer UDF Il n'y a aucun moyen pris en charge directement à « stabiliser » une UDF. Ainsi, la plupart des clients sont mieux pré-calcul de simplement la valeur de retour de l'UDF, ce qui rend cette valeur littérale disponible en tant que paramètre fourni par le client, dans un GTT, etc.

Kludge

CURRENT_TRANSACTION et CURRENT_TIMESTAMP pris ensemble identifier efficacement une requête individuelle, au moins à la précision de CURRENT_TIMESTAMP. (Encore une fois en supposant que l'horloge est en UTC, de peur que répéter le temps lui-même lors d'un changement d'heure d'été).

Dans cet esprit, une procédure stockée sélectionnable pourrait mettre en cache la valeur de retour de l'UDF une chaîne à l'aide RDB $ SET_CONTEXT et RDB $ GET_CONTEXT, le stockage dans le contexte de user_transaction et la saisie au large de CURRENT_TIMESTAMP. Ajouter un peu de logique supplémentaire pour élaguer le nombre d'entrées enregistrées sous user_transaction, aussi. Beurk.

Autres conseils

AFAIK, vous ne pouvez pas marquer une UDF comme constante ou inmutable dans Firebird, mais comme une solution de contournement, vous pouvez compter sur une vue en ligne (tableau alias dérivée) pour obtenir ce que vous voulez: sélectionner la valeur juste une fois et l'utiliser comme une constante dans vos résultats. Je n'ai pas UDF à portée de main pour faire un test, alors peut-être une erreur de syntaxe, mais j'espère que vous attrapez l'idée derrière ceci:

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;

Vous pouvez également compter sur une procédure stockée sélectionnable pour exécuter le udf une fois et ajouter la colonne aux résultats d'une requête donnée

Modifier Comme l'a demandé, je compris la procédure stockée:

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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top