2010-11-16 17 views
2

Ok, que j'ai une requête:requête Picking basée sur le paramètre dans Oracle PL/SQL

SELECT * FROM TABLE_AWESOME WHERE YEAR = :AMAZINGYEAR; 

Ce qui fonctionne très bien. Mais disons que je veux être en mesure de retourner soit simplement ces résultats ou tous les résultats basés sur une liste déroulante. (Par exemple, la baisse de baisse aurait 2008, 2009, toutes les années)

j'ai décidé de lutter contre ledit problème avec PL/SQL avec le format suivant:

DECLARE 
    the_year VARCHAR(20) := &AMAZINGYEAR; 
BEGIN 
    IF the_year = 'ALL' THEN 
     SELECT * FROM TABLE_AWESOME; 
    ELSE 
     SELECT * FROM TABLE_AWESOME WHERE YEAR = the_year; 
    END IF; 
END; 

Malheureusement, cela ne fonctionne pas. Je reçois des erreurs comme "une clause INTO est attendue dans cette instruction SELECT". Je suis complètement nouveau à PL/SQL donc je pense que j'attends juste trop de lui. J'ai examiné la documentation mais je n'ai trouvé aucune raison pour que cela ne fonctionne pas comme je l'ai fait. La requête que j'utilise actuellement est beaucoup plus compliquée que celle-ci, mais je veux rester aussi simple que possible afin d'obtenir rapidement une réponse.

Merci à l'avance :)

+0

Vous devez faire quelque chose avec le résultat de la requête. que veux-tu faire après? –

+0

Je veux juste qu'il renvoie les résultats. Je lie aux résultats avec un SqlDataSource dans ASP.NET. Je n'ai pas l'intention de créer ceci comme une procédure, je veux juste exécuter le bloc et lier aux résultats. Est-ce possible? – clifgriffin

+0

@InSane ... complètement faux. –

Répondre

3

Vous pouvez le faire avec une requête, quelque chose comme:

SELECT * FROM TABLE_AWESOME WHERE (? = 'ALL' OR YEAR = ?) 

et transmettre l'argument deux fois.

+0

Vous êtes génial. Je vous remercie. – clifgriffin

+0

Ne le faites pas à moins que votre nombre total de lignes soit proche du nombre de lignes pendant 1 an. Si vous avez 20 ans ou plus là-dedans ... ne l'utilisez pas. Vous aurez des problèmes de performance. Vous devez toujours expliquer ce que vous essayez de faire. –

1

Je ne sais pas d'utiliser un SqlDataSource, mais vous pouvez certainement le faire via le system.data.oracle ou les clients d'Oracle.

Vous devez faire cela via un bloc anonyme en asp.net

VAR SYS1 REFCURSOR; 
VAR SYS2 REFCURSOR; 

DECLARE 
    FUNCTION CURSORCHOICE(ITEM IN VARCHAR2) RETURN SYS_REFCURSOR IS 
     L_REFCUR SYS_REFCURSOR; 
    returnNum VARCHAR2(50); 
    BEGIN 
     IF upper(item) = 'ALL' THEN 
      OPEN L_REFCUR FOR 
      SELECT level FROM DUAL 
      CONNECT BY LEVEL < 15 ; 
     ELSE 
      OPEN L_REFCUR FOR 
      SELECT 'NONE' FROM DUAL ; 
     END IF; 
     RETURN L_REFCUR; 
    END ; 
BEGIN 
:SYS1 := CURSORCHOICE('ALL'); 
:SYS2 := CURSORCHOICE('NOT ALL'); 
end ; 
/
PRINT :SYS1 ; 
PRINT :SYS2 ; 

alors vous simplement créer un param de sortie (de type refcursor) - au lieu des refcursors var sys #) et à peu près juste modifier le code ci-dessus.

je répondu à une question similaire à obtenir un refcuror bloc anonyme ici How to return a RefCursor from Oracle function?

0

Ce type de paramètre doit être traité à partir de votre code afin que votre objet OracleCommand exécute uniquement les requêtes soit.

using (var connection = new OracleConnection(connString)) { 
    connection.Open(); 

    string sql = "select * from table_awesome"; 
    sql = string.Concat(sql, theYear.Equals(@"ALL") ? string.Empty : " where year = :pYear") 

    using (var command = connection.CreateCommand()) { 
     command.CommancText = sql; 
     command.CommandType = CommandType.Text; 
     var parameter = command.CreateParameter(); 
     parameter.Name = @":yearParam"; 
     parameter.Direction = ParameterDirection.Input; 
     parameter.Value = theYear; 

     var reader = command.ExecuteQuery(); 

     if (!reader.HasRows) return; 

     while (reader.Read()) { 
      // Extract your data from the OracleDataReader instance here. 
     } 
    } 
} 
3

En PL/SQL vous devez SELECT ... INTO quelque chose, dont vous avez besoin pour pouvoir retourner au client; cela pourrait être un curseur de référence comme le démontre tanging. Cela peut compliquer le client.

Vous pouvez le faire dans SQL plutôt avec quelque chose comme:

SELECT * FROM TABLE_AWESOME WHERE :AMAZING_YEAR = 'ALL' OR YEAR = :AMAZINGYEAR; 

... bien que vous pourriez avoir besoin de prendre en charge sur les index; Je regarde le plan d'exécution avec les deux types d'arguments pour vérifier qu'il ne fait pas quelque chose d'inattendu.

+0

Merci. J'ai voté en ce sens, mais comme Jim a reçu sa réponse un peu avant la vôtre, je lui ai posé la question. :) – clifgriffin

4

Il existe un réel danger dans les requêtes offertes par Jim et Alex.

Supposons que vous ayez 20 ans de données, une requête sur YEAR = renvoie 5% des blocs. Je dis blocs et non lignes parce que je suppose que les données sont ajoutées à cette date afin que le facteur de regroupement est élevé.

Si vous voulez 1 an, vous voulez que l'optimiseur utilise un index sur l'année pour trouver ces 5% de lignes.

Si vous voulez toutes les années, vous voulez que l'optimiseur utilise une analyse de table complète pour obtenir chaque ligne.

Sommes-nous bons jusqu'à maintenant?

Une fois que vous mettez cela en production, la première fois qu'Oracle charge la requête, il pointe sur la variable de liaison et formule un plan basé sur cela. Donc, disons que le premier chargement est 'Tout'.

Très bien, le plan est une analyse de table complète (FTS) et ce plan est mis en cache et vous obtenez toutes les lignes en 5 minutes. Pas de gros problème.

La prochaine exécution, vous dites 1999. Mais le plan est mis en cache et utilise donc un FTS pour obtenir seulement 5% des lignes et cela prend 5 minutes. "Hmmm ... l'utilisateur dit, c'était beaucoup moins de lignes et le même temps." Mais c'est bien ... c'est juste un rapport de 5 minutes ... la vie est un peu lente quand ce n'est pas obligatoire mais personne ne crie. Cette nuit-là, les tâches par lots soufflent cette requête hors du cache et le matin, le premier utilisateur demande 2001. Oracle vérifie le cache, pas là, jette un œil à la variable, 2001. Ah, le meilleur plan pour cela est un balayage d'index. et ce plan est mis en cache. Les résultats reviennent en 10 secondes et envolent l'utilisateur. La personne suivante, qui est normalement la première, fait le rapport "ALL" du matin et la requête ne revient jamais.

POURQUOI?

Parce qu'il obtient chaque ligne en regardant à travers l'index .... horribles boucles imbriquées. Le rapport de 5 minutes est maintenant à 30 et compte.

Votre message original a la meilleure réponse. Deux requêtes, de cette façon les deux auront TOUJOURS le meilleur plan, lier la variable peeking ne vous tue pas.

Le problème que vous rencontrez est juste un problème fondamental d'Oracle. Vous exécutez une requête à partir d'un outil et récupérez les résultats dans l'outil. Si vous mettez une instruction select dans un bloc pl/sql, vous devez faire quelque chose avec. Vous devez le charger dans un curseur, un tableau ou une variable. Cela n'a rien à voir avec le fait que vous ayez tort et qu'ils ont raison ... c'est juste un manque de compétences pl/sql.

+0

+1 bon conseil, mais pas toujours un problème. Pour tout ce que nous savons, la table a une contrainte UNIQUE sur l'année. par exemple. ce pourrait être un tableau récapitulatif. –

+0

D'accord, je n'ai pas assez exprimé mon avertissement * 8-) –

+0

C'est pourquoi j'aime ce site ... J'apprends toujours des choses. Je ne pense pas que cela sera problématique dans mon cas. La requête que j'utilise retourne toutes les classes qu'un étudiant a prises pour tous les termes qu'ils ont étudiés. Donc, vous regardez comme 50 lignes max. Et, environ 6 lignes en moyenne. – clifgriffin