Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Ist es möglich, GROUP BY mit Bind-Variablen zu verwenden?

Ich schlage vor, die Anweisung so umzuschreiben, dass es nur ein Bindeargument gibt. Dieser Ansatz ist etwas hässlich, gibt aber die Ergebnismenge zurück:

select max(col1) 
     , f_col2
  from (
         select col1
              , f(? ,col2) as f_col2 
           from t
       )
 group
    by f_col2

Diese neu geschriebene Anweisung hat nur einen Verweis auf ein einziges Bindeargument, sodass das DBMS jetzt sieht, dass die Ausdrücke in der GROUP BY-Klausel und der SELECT-Liste identisch sind.

HTH

[BEARBEITEN]

(Ich wünschte, es gäbe einen schöneren Weg, deshalb bevorzuge ich den von Oracle verwendeten Ansatz mit benannten Bindeargumenten. Mit dem Perl-DBI-Treiber werden Positionsargumente in benannte Argumente in der Anweisung umgewandelt, die tatsächlich an Oracle gesendet wird.)

Ich habe das Problem zuerst nicht gesehen, ich habe die ursprüngliche Frage nicht verstanden. (Anscheinend haben es mehrere andere Leute auch vermisst.) Aber nachdem ich einige Testfälle ausgeführt hatte, dämmerte es mir, was das Problem war, was die Frage funktionierte.

Lassen Sie mich sehen, ob ich das Problem beschreiben kann:wie man bekommt, dass zwei getrennte (positionelle) Bindungsargumente (vom DBMS) so behandelt werden, als ob es zwei Verweise auf dasselbe (benannte) Bindungsargument wären.

Das DBMS erwartet, dass der Ausdruck in GROUP BY mit dem Ausdruck in der SELECT-Liste übereinstimmt. Aber die beiden Ausdrücke werden auch dann als VERSCHIEDEN angesehen, wenn die Ausdrücke identisch sind, wenn der einzige Unterschied darin besteht, dass jeder Ausdruck auf eine andere Bindungsvariable verweist. (Wir können einige Testfälle demonstrieren, die zumindest einige DBMS zulassen, aber es gibt allgemeinere Fälle, die eine Ausnahme auslösen.)

An dieser Stelle lautet die kurze Antwort, das hat mich ratlos gemacht. Der Vorschlag, den ich habe (der möglicherweise keine tatsächliche Antwort auf die ursprüngliche Frage ist), besteht darin, die Abfrage neu zu strukturieren.

[/BEARBEITEN]

Ich kann weitere Details bereitstellen, wenn dieser Ansatz nicht funktioniert oder wenn Sie ein anderes Problem haben, es herauszufinden. Oder wenn es ein Problem mit der Leistung gibt (ich kann sehen, dass der Optimierer einen anderen Plan für die neu geschriebene Abfrage auswählt, obwohl er die angegebene Ergebnismenge zurückgibt. Für weitere Tests müssten wir wirklich wissen, welches DBMS, welcher Treiber, Statistiken usw.)

BEARBEITEN (achteinhalb Jahre später)

Ein weiterer Versuch einer Abfrageumschreibung. Auch hier ist die einzige Lösung, die mir einfällt, eine Abfrage mit einem Bind-Platzhalter. Dieses Mal fügen wir es in eine Inline-Ansicht ein, die eine einzelne Zeile zurückgibt, und verbinden diese mit t. Ich kann sehen, was es tut; Ich bin mir nicht sicher, wie der Oracle-Optimierer dies sehen wird. Möglicherweise möchten (oder müssen) wir eine explizite Konvertierung durchführen, z. TO_NUMBER(?) AS param , TO_DATE(?,'...') AS param , TO_CHAR(?) AS param , abhängig vom Datentyp des Bindungsparameters und dem Datentyp, der von der Ansicht zurückgegeben werden soll.)

So würde ich es in MySQL machen. Die ursprüngliche Abfrage in meiner Antwort führt den Join-Vorgang innerhalb der Inline-Ansicht aus (MySQL abgeleitete Tabelle ). Und wir möchten vermeiden, eine von Hughjass abgeleitete Tabelle zu materialisieren, wenn wir dies vermeiden können. Andererseits würde MySQL die ursprüngliche Abfrage wahrscheinlich solange gleiten lassen wie sql_mode enthält nicht ONLY_FULL_GROUP_BY . MySQL würde uns auch den FROM DUAL fallen lassen )

  SELECT MAX(t.col1)
       , f( v.param ,t.col2)
    FROM t
   CROSS
    JOIN ( SELECT ? AS param FROM DUAL) v
   GROUP
      BY f( v.param ,t.col2)

Laut der Antwort von MadusankaD hat Oracle in den letzten acht Jahren die Unterstützung für die Wiederverwendung der gleichnamigen Bindungsparameter im JDBC-Treiber und die Beibehaltung der Äquivalenz hinzugefügt. (Ich habe das nicht getestet, aber wenn das jetzt funktioniert, dann großartig.)