2014-01-28 12 views
6

Betrachten wir eine deterministische Funktion wie:Wann verwendet eine deterministische Funktion den vorherigen berechneten Wert?

CREATE OR REPLACE FUNCTION SCHEMA.GET_NAME(ss_id nvarchar2 
        ) RETURN nvarchar2 DETERMINISTIC IS 
    tmpVar nvarchar2(500); 
    BEGIN 

     select name into tmpvar from logistics.organization_items 
     where id = ss_id ; 
     return tmpvar ; 

    END ss_name; 

Mit Toad ich die SCHEMA.GET_NAME(1) genannt und es gibt A. Ich habe dann den Wert aus der Tabelle von A zu B geändert und die SCHEMA.GET_NAME(1) zurückgegeben B zurückgerufen.

Es ist ein gutes Ergebnis. Aber ich fürchte, der Wert nicht aktualisiert wird nach this page in the documentation, die sagte:

Wenn Oracle Database eine deterministische Funktion in einem dieser Kontexten begegnet, versucht er zuvor berechnete Ergebnisse zu verwenden, wenn möglich, anstatt reexecuting die Funktion. Wenn Sie anschließend die Semantik der Funktion ändern, müssen Sie alle abhängigen funktionsbasierten Indizes und materialisierten Sichten manuell neu erstellen.

In welchen Situationen würde der Wert von GET_NAME(1) Rückkehr ein alter gecached Wert (A statt B)?

Antwort

6

Wenn Sie aus einer Tabelle auswählen, sind die Ergebnisse Ihrer Funktion nicht deterministisch. A deterministic system ist eine, die immer die gleiche Ausgabe produzieren, unter den gleichen Anfangsbedingungen.

Es ist möglich, die Informationen in einer Tabelle zu ändern, daher ist eine Funktion, die aus einer Tabelle auswählt, nicht deterministisch. Ein Zitat aus dem PL/SQL Language Reference:

diese Klausel nicht angeben, um eine Funktion zu definieren, die Paketvariablen verwendet oder dass greift auf die Datenbank in irgendeiner Weise, die die Rückkehr Ergebnis der Funktion beeinträchtigen könnten. Die Ergebnisse werden nicht erfasst, wenn die Datenbank entscheidet, die Funktion nicht erneut auszuführen.

Mit anderen Worten, Oracle garantiert nicht, dass die Ergebnisse der Funktion genau sein werden (sie könnten nur sein). Wenn Ihr Tisch statisch ist und sich wahrscheinlich nie ändern wird, dann sollte es in Ordnung sein, aber darauf möchte ich mich nie verlassen. Um Ihre Frage zu beantworten, gehen Sie nicht davon aus, dass Oracle etwas anderes als den zwischengespeicherten Wert innerhalb derselben Transaktion/Sitzung zurückgibt.

Wenn Sie dies beschleunigen müssen, gibt es zwei Möglichkeiten. Überprüfen Sie zuerst, ob Sie einen Index für ID haben!

  1. Einfach JOIN zu dieser Tabelle. Wenn Ihre Funktion nur dies ist, dann ist es nicht notwendig, dass die Funktion existiert.

  2. Verwenden Sie scalar sub-query caching (nicht unbedingt möglich, aber den Versuch wert).

    select (select get_name(:id) from dual) 
        from your_table 
    

    Oracle erstellt einen In-Memory-Hash der Ergebnisse der Funktion, wie einen Ergebnis-Cache. Wenn Sie dieselbe Funktion mehrmals ausführen, trifft Oracle den Cache und nicht die Funktion.

+0

Danke für Ihre vollständige Antwort. Auch ich mag es jetzt, wie oft der Cache für diese Funktionen aktualisiert wird? Im Rahmen der Ausführung einer Abfrage? Nach einem Zeitintervall? Auf Speicherbedarf um den Cache zu befreien? – mehrandvd

+0

Oder vielleicht sind Caches nur während einer Sitzung gültig. Also für eine neue Sitzung wird es Updates geben? – mehrandvd

+2

@Mehrandvd - Ich kenne die Ins-und-Outs dieses Features nicht, aber ich habe das Gefühl, es ist wie bei den meisten anderen Optimierungen: Es passiert, wenn der Optimierer sich danach fühlt. Mit anderen Worten, es gibt ** NO WAY **, um zu garantieren, dass es zwischengespeichert/aktualisiert wird (es gibt einige Wahrscheinlichkeiten, meistens mit Caching, aber keine Garantien). Dies ist vergleichbar mit dem Ausführen einer Abfrage ohne 'ORDER BY' - Sie erhalten die Reihenfolge, die der Optimierer Ihnen zu geben entscheidet, nicht das, was Sie unbedingt haben wollen/erwarten. –

1

Ben Antwort bringt es auf, und ich möchte nur hinzufügen, dass die Art und Weise Sie DETERMINISTIC Schlüsselwort in Ihrer Funktion ist nicht richtig - im Auge behalten, dass Sie den Wert aus einer Tabelle lesen und dann zurückkehren das gleiche für den Benutzer.

Eine deterministische Funktion sollte in Fällen verwendet werden, in denen Sie einen Ausdruck über eine feste Eingabe auswerten, z. B. wenn Sie eine Teilzeichenfolge oder Groß-/Kleinschreibung für die Eingabezeichenfolge zurückgeben müssen. Programmatisch wissen Sie, dass für die gleiche Eingabe die Kleinbuchstabe-Funktion immer den gleichen Wert zurückgibt, und Sie möchten daher das Ergebnis zwischenspeichern (mit deterministischem Schlüsselwort).

Wenn Sie einen Wert aus einer Tabelle lesen, hat Oracle keine Möglichkeit zu wissen, dass der Wert in der Spalte nicht geändert wurde, und so lieber die Funktion ausführen und nicht auf das zwischengespeicherte Ergebnis abhängen (was sinnvoll ist)

0

Können Sie Ihrer Funktion einen Timestamp-Parameter hinzufügen? Übergeben Sie dann sysdate an die Funktion von wo auch immer Sie es aufrufen.

Auf diese Weise wird das Ergebnis effektiv zwischengespeichert und Sie vermeiden, dass die Funktion immer wieder ausgeführt wird, wenn sie in der Regel den gleichen Wert innerhalb einer bestimmten Transaktion zurückgibt.

0

Die Bemerkung von Erez ist die Antwort, die ich suchte. Bevor Sie die Abfrage oder die PLSQL-Unit ausführen, können Sie mit dieser Lösung erzwingen, die Funktion erneut auszuführen, nachdem Sie die ret-Werte der Funktion zurückgesetzt haben (z. B. Ändern einer Paketvariable). Ich benutze dies für: select ... von big_table_vw;

wo

erstellen Ansicht big_table_vw als Auswählen ... (analytische Funktionen) von big_table wo last_mutated> = get_date();

In meinem Fall enthält die big_table_vw Fensterfunktionen, die verhindern, dass Oracle das Prädikat in die Ansicht verschiebt.

+0

Korrektur: Ich habe gerade versucht, die Funktion viele Male ohne einen Zeitstempel aufzurufen, und das Ergebnis ist, dass die Funktion einmal für jede SQL-Anweisung aufgerufen wird. Ein Zeitstempel wird also nicht benötigt. – Rik

0

Dies ist eine späte Folge einer lang beantworteten Frage, aber ich wollte nur hinzufügen, dass Oracle einen Cache-Mechanismus für Funktionen mit veränderlichen Abhängigkeiten bereitstellt. ist eine Alternative zu DETERMINISTIC, die es Oracle ermöglicht, zwischengespeicherte Funktionsergebnisse jedes Mal zu verwerfen, wenn ein referenziertes Objekt geändert wird.

Auf diese Weise können Sie kostspielige Berechnungen gegen selten aktualisierte Objekte im Cache speichern, mit der Gewissheit, dass zwischengespeicherte Ergebnisse keine falschen Ergebnisse liefern.

Hier ist ein Beispiel unter Verwendung von mythologischen Monstern:

CREATE TABLE MONSTER (
    MONSTER_NAME VARCHAR2(100) NOT NULL PRIMARY KEY 
); 

INSERT INTO MONSTER VALUES ('Chthulu'); 
INSERT INTO MONSTER VALUES ('Grendel'); 
INSERT INTO MONSTER VALUES ('Scylla'); 
INSERT INTO MONSTER VALUES ('Nue'); 
COMMIT; 

CREATE OR REPLACE PACKAGE MONSTER_PKG 
IS 
    FUNCTION IS_THIS_A_MONSTER(P_MONSTER_NAME IN VARCHAR2) 
    RETURN BOOLEAN RESULT_CACHE; 
END MONSTER_PKG; 
/

CREATE OR REPLACE PACKAGE BODY MONSTER_PKG 
IS 
    FUNCTION IS_THIS_A_MONSTER(P_MONSTER_NAME IN VARCHAR2) 
    RETURN BOOLEAN 
    RESULT_CACHE RELIES_ON (MONSTER) 
    IS 
    V_MONSTER_COUNT NUMBER(1, 0) := 0; 
    BEGIN 
     SELECT COUNT(*) 
     INTO V_MONSTER_COUNT 
     FROM MONSTER 
     WHERE MONSTER_NAME = P_MONSTER_NAME; 
     RETURN (V_MONSTER_COUNT > 0); 
    END; 
END MONSTER_PKG; 
/

Wenn ein Szenario wie das unten auftritt, alle vorhandenen Cache ungültig gemacht und ein neuer Cache kann dann wieder aufgebaut werden.

BEGIN 
    DBMS_OUTPUT.PUT_LINE('Is Kraken initially a monster?'); 
    IF MONSTER_PKG.IS_THIS_A_MONSTER('Kraken') 
    THEN 
    DBMS_OUTPUT.PUT_LINE('Kraken is initially a monster'); 
    ELSE 
    DBMS_OUTPUT.PUT_LINE('Kraken is not initially a monster'); 
    END IF; 
    INSERT INTO MONSTER VALUES ('Kraken'); 
    COMMIT; 
    DBMS_OUTPUT.PUT_LINE('Is Kraken a monster after update?'); 
    IF MONSTER_PKG.IS_THIS_A_MONSTER('Kraken') 
    THEN 
    DBMS_OUTPUT.PUT_LINE('Kraken is now a monster'); 
    ELSE 
    DBMS_OUTPUT.PUT_LINE('Kraken is not now a monster'); 
    END IF; 
END; 
/

Ist Kraken zunächst ein Monster?
Kraken ist zunächst kein Monster
Ist Kraken ein Monster nach dem Update?
Kraken ist jetzt ein Monster

Verwandte Themen