Kann ich mich darauf verlassen, dass Funktionen zuerst in SQL ausgeführt werden?

9

Bitte beachten Sie das folgende Skript:

create or replace function f(p_limit in integer) return integer as
begin
  set_global_context ('limit', p_limit);
  return p_limit;
end;
/

create view v as 
select level as val from dual connect by level<=sys_context('global_context','limit');

select f(2), v.* from v;

/*
F(2)                   VAL                    
---------------------- ---------------------- 
2                      1                      
2                      2                      
*/

select f(4), v.* from v;

/*
F(4)                   VAL                    
---------------------- ---------------------- 
4                      1                      
4                      2                      
4                      3                      
4                      4                      
*/

Kann ich mich darauf verlassen f(x), ausgeführt zu werden, bevor der Kontext in der Ansicht gelesen wird, wie es in diesem Testfall unter 10.2 der Fall war?

Jack sagt, versuchen Sie es mit topanswers.xyz
quelle
Ich kann nicht anders, als zu glauben, dass ein Login-Trigger besser geeignet ist (wenn das Level immer gleich ist)
Philᵀᴹ
@Phil dies ist nur ein Beispiel - ich verwende sys_context, um eine Ansicht zu parametrisieren, und der Parameter wird jedes Mal anders sein. Wenn Sie wissen, wie Sie einen globalen Kontext aus SQL festlegen können, ohne so herumzuspielen, würde mich das auch interessieren!
Jack sagt, versuchen Sie topanswers.xyz
1
@ JackDouglas: Das Parametrisieren einer Ansicht ist eine Idee, die sich für mich nicht richtig "anfühlt". Unter MSSQL können Sie versuchen, eine benutzerdefinierte Funktion zu verwenden, die eine Ergebnismenge (anstelle eines Werts) zurückgibt. Dies können Sie dann SELECT stuff FROM dbo.FuncReturningTable(param)oder ähnliches. Oracle hat wahrscheinlich eine gleichwertige Funktionalität. Wenn ich dies über große Datenmengen verwende, würde ich die Leistung sorgfältig überwachen: Ich bin mir nicht sicher, wie hell der Abfrageplaner sein müsste, um aus einer solchen Syntax einen effizienten Plan zu erstellen.
David Spillett
@David-Parameter für das Erstellen einer Ansicht werden normalerweise mit sys_context ausgeführt. Normalerweise legen Sie den Kontext vor dem Ausführen der Abfrage fest (z. B. mit etwas PL / SQL). Oracle verfügt über Set-Return- und / oder Pipeline-Funktionen, die jedoch nicht der "normale" Weg sind, dies zu erreichen. Um es klar zu sagen, ich denke die Antwort auf die Frage im Titel ist "nein" - ich habe mich nur gefragt, ob jemand es besser weiß.
Jack sagt, versuchen Sie es mit topanswers.xyz

Antworten:

8

Nein.

Wenn Sie Ihre Ansicht mit der Kontextfilterung anhand der where-Klausel (anstelle der connect by) neu schreiben, erhalten Sie den zuvor festgelegten Wert für den Kontext:

create table t as 
 select rownum r from dual connect by level <= 10;

create or replace view v as 
  select r val from t where r <=sys_context('global_context','limit');

select f(2), v.* from v;

F(2) VAL
---- ---
   2   1 
   2   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 
   4   3 
   4   4 

Da die where-Klausel vor der Auswahl der Spalten ausgewertet wird, wird der an die Funktion übergebene Wert erst nach dem Lesen des Kontexts festgelegt. Der Speicherort des Aufrufs sys_context in Ihrer Abfrage (auswählen, wo, gruppieren nach usw.) wirkt sich genau darauf aus, wann dieser Wert festgelegt wird.

Chris Saxon
quelle
+1, das ist in meinem Buch so ziemlich "Fall abgeschlossen", danke.
Jack sagt, versuchen Sie es mit topanswers.xyz
2

Im Allgemeinen können Sie nicht sicher annehmen, in welcher Reihenfolge Ihr DBMS bei der Auswertung einer einzelnen SQL-Anweisung vorgeht. Aus diesem Grund lassen viele DBMS nicht zu, dass auf diese Weise verwendete Funktionen Nebenwirkungen haben (dh MSSQL erlaubt Funktionen nicht, den globalen / Verbindungsstatus festzulegen, den Sie dort ausführen, oder den Tabelleninhalt zu ändern). Eine Reihe von Anweisungen muss so ausgeführt werden, dass sie von einem Schritt zum nächsten sinnvoll sind (dh sie werden seriell ausgeführt oder so, dass Sie nicht erkennen können, dass dies nicht der Fall ist), aber innerhalb einer einzelnen Anweisung der Abfrageplaner hat freie Hand, solange es keine Mehrdeutigkeit einführt, wo es noch nicht existiert (in Ihrem Beispiel existiert bereits Mehrdeutigkeit, weil die Funktion einen Nebeneffekt hat, der die Ansicht beeinflusst).

Wenn der Abfrageplaner hell genug wäre, um zu erkennen, dass die Ansicht von den Nebenwirkungen der Funktion betroffen ist, was würde er tun, wenn Sie einer anderen Ansicht beitreten würden, die diese Funktion möglicherweise mit unterschiedlichen Eingabewerten aufruft? Es könnte ziemlich schnell sehr haarig werden - deshalb sollten Funktionen in jedem Programmierkontext im Allgemeinen keine Auswirkungen haben, die über ihre eigene Ausgabe hinausgehen.

In diesem speziellen Beispiel würde ich sagen, dass es unwahrscheinlich ist, dass f (x) zuerst aufgerufen wird, da es sich um den "Anzeige" -Teil der Anweisung handelt: Die Ergebnismenge aus der Ansicht wird wahrscheinlich vor Funktionen innerhalb der abgerufen Die Liste der zurückzugebenden Spalten wird ausgewertet. Dies hängt natürlich vom verwendeten DBMS ab: Ich bin kein Oracle-Experte, und Ihre Testergebnisse zeigen, dass die Funktion in diesen Fällen anscheinend zuerst aufgerufen wird. Ich würde mich jedoch davor hüten, mich auf die Ausführungsreihenfolge innerhalb einer einzelnen SQL-Anweisung zu verlassen - auch wenn sie in zukünftigen Revisionen immer so funktioniert, wie Sie es derzeit erwarten (es sei denn, es ist offiziell irgendwo dokumentiert, dass die Ausführung immer stattfinden wird) hier herum).

David Spillett
quelle
2
Gute Antwort, aber ich habe das Gefühl, dass Jack nach einer endgültigen technischen Oracle-Antwort sucht.
Philᵀᴹ
1

Die Dokumentation verspricht nur, dass "der Optimierer zuerst Ausdrücke und Bedingungen, die Konstanten enthalten, so vollständig wie möglich bewertet." ( 10.2 , 11.2 ). Es kann nicht garantiert werden, dass zuerst ein bestimmter Ausdruck ausgewertet wird oder dass diese Reihenfolge nicht von Zeit zu Zeit geändert wird (ein neues Patchlevel innerhalb derselben Version?).

Stephen Kendall
quelle
+1 ausgezeichnet, danke (obwohl diese Dokumente nicht ganz mit Chris 'Antwort
übereinstimmen
1
Der Unterschied besteht darin, ob die Funktion in der where-, select- oder einer anderen Klausel aufgerufen wird. Funktionen im Auswahlbereich wirken sich nicht auf die Optimierungsentscheidungen aus (es sei denn, es handelt sich um eine Unterabfrage), sodass diese erst nach dem Abrufen der Ergebnisse ausgewertet werden müssen. Funktionen in der where-Klausel wirken sich jedoch auf die verwendete Join-Methode aus und müssen daher so schnell wie möglich ausgewertet werden.
Chris Saxon
@ Chris ist diese Erfahrung zu sprechen oder haben Sie das irgendwo aus den Dokumenten bekommen?
Jack sagt, versuchen Sie topanswers.xyz
Ich kann keine Dokumentreferenz finden. Wenn ich in der where-Klausel aufgerufen werde, um eine einzelne Tabelle zu filtern, wird meiner Erfahrung nach für jede Zeile (unter der Annahme eines FTS) zugegriffen, jedoch nur für die zurückgegebenen Zeilen, wenn sie in der Auswahlliste enthalten sind. Da der Ausführungsplan während des Parsens festgelegt wird, bedeutet dies, dass Funktionen in select ihn nicht beeinflussen können. Ein Testfall, um dies zu überprüfen, könnte durchgeführt werden, indem eine Funktion erstellt wird, die einen Zähler (in einem Paket oder einer Tabelle) setzt, und die Ausgabe basierend darauf verglichen wird, wo in der Abfrage sie platziert ist.
Chris Saxon