Oracle
 sql >> Base de données >  >> RDS >> Oracle

Est-il possible d'utiliser GROUP BY avec des variables liées ?

Je suggère de réécrire l'instruction afin qu'il n'y ait qu'un seul argument de liaison. Cette approche est un peu laide, mais renvoie le jeu de résultats :

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

Cette instruction réécrite fait référence à un seul argument de liaison, donc maintenant le SGBD voit les expressions dans la clause GROUP BY et la liste SELECT sont identiques.

HTH

[MODIFIER]

(J'aurais aimé qu'il y ait une méthode plus jolie, c'est pourquoi je préfère l'approche d'argument de liaison nommé qu'Oracle utilise. Avec le pilote Perl DBI, les arguments positionnels sont convertis en arguments nommés dans l'instruction réellement envoyée à Oracle.)

Je n'ai pas vu le problème au début, je n'ai pas compris la question initiale. (Apparemment, plusieurs autres personnes l'ont également manqué.) Mais après avoir exécuté quelques cas de test, j'ai compris quel était le problème, quelle était la question qui fonctionnait.

Voyons si je peux énoncer le problème :comment faire en sorte que deux arguments de liaison distincts (positionnels) soient traités (par le SGBD) comme s'il s'agissait de deux références au même argument de liaison (nommé).

Le SGBD attend que l'expression dans GROUP BY corresponde à l'expression dans la liste SELECT. Mais les deux expressions sont considérées comme DIFFÉRENTES même lorsqu'elles sont identiques, lorsque la seule différence est que chaque expression fait référence à une variable de liaison différente. (Nous pouvons démontrer certains cas de test qu'au moins certains SGBD autoriseront, mais il existe des cas plus généraux qui lèveront une exception.)

À ce stade, la réponse courte est que cela me laisse perplexe. La suggestion que j'ai (qui peut ne pas être une réponse réelle à la question d'origine) est de restructurer la requête.

[/MODIFIER]

Je peux fournir plus de détails si cette approche ne fonctionne pas, ou si vous avez un autre problème pour le comprendre. Ou s'il y a un problème de performances (je peux voir l'optimiseur choisir un plan différent pour la requête réécrite, même s'il renvoie le jeu de résultats spécifié. Pour des tests supplémentaires, nous aurions vraiment besoin de savoir quel SGBD, quel pilote, statistiques, etc.)

MODIFIER (huit ans et demi plus tard)

Une autre tentative de réécriture de requête. Encore une fois, la seule solution que je propose est une requête avec un espace réservé de liaison. Cette fois, nous la collons dans une vue en ligne qui renvoie une seule ligne et la joignons à t. Je peux voir ce qu'il fait; Je ne sais pas comment l'optimiseur Oracle verra cela. Nous pouvons vouloir (ou avoir besoin) de faire une conversion explicite, par ex. TO_NUMBER(?) AS param , TO_DATE(?,'...') AS param , TO_CHAR(?) AS param , en fonction du type de données du paramètre de liaison et du type de données que nous voulons renvoyer à partir de la vue.)

C'est comme ça que je le ferais dans MySQL. La requête d'origine dans ma réponse effectue l'opération de jointure dans la vue en ligne (MySQL table dérivée ). Et nous voulons éviter de matérialiser une table dérivée hughjass si nous pouvons l'éviter. Là encore, MySQL laisserait probablement glisser la requête d'origine tant que sql_mode n'inclut pas ONLY_FULL_GROUP_BY . MySQL nous laisserait également supprimer le FROM DUAL )

  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)

Selon la réponse de MadusankaD, au cours des huit dernières années, Oracle a ajouté la prise en charge de la réutilisation des mêmes paramètres de liaison nommés dans le pilote JDBC et du maintien de l'équivalence. (Je n'ai pas testé cela, mais si cela fonctionne maintenant, tant mieux.)