Précédent   Forum des professionnels en informatique > Bases de données > Oracle
Oracle Forum Oracle : le serveur, les outils, ... Voir F.A.Q Oracle Tutoriels Oracle
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 27/07/2011, 17h07   #1
Membre habitué
 
Inscription : décembre 2004
Messages : 643
Détails du profil
Informations forums :
Inscription : décembre 2004
Messages : 643
Points : 104
Points : 104
Par défaut Oracle : Case dans Where qui retourne une liste

Bonjour,

Je suis dans une procédure stockée.
Suivant la valeur de ma variable V_PARAM, la liste des identifiants en paramètre va être variable (cf exemple ci-dessous).

Une liste, ça ne marche pas :
Code :
1
2
3
4
select t.champ1, t.champ2
from MATABLE t 			
where t.champ3 in 
(select case when V_PARAM=3 then (120, 2) else (5) end from dual)
Une valeur simple, ça marche (mais c'est pas ce que je veux)
Code :
1
2
3
4
select t.champ1, t.champ2
from MATABLE t 			
where t.champ3 in 
(select case when V_PARAM=3 then 120 else 5 end from dual)
Si c'est pas possible, je suis aussi preneur d'une solution qui me permette carrément de faire des sélect différents de ce type... à l'intérieur d'une boucle for :
Code :
1
2
3
4
5
6
7
8
9
FOR c_table IN (
--cas1 :
SELECT t.champ1, t.champ2
FROM MATABLE t 			
WHERE t.champ3 IN (120, 2) 
--cas2 ?
)loop
--bla, bla
end loop;

Merci pour votre aide.
stof est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/07/2011, 22h10   #2
Membre Expert
 
Inscription : août 2008
Messages : 1 271
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 1 271
Points : 1 929
Points : 1 929
Effectivement tu ne peux pas utiliser CASE avec de multiples valeurs, ou du moins je ne vois pas comment à part remplacer IN par plusieurs OR.
Citation:
Si c'est pas possible, je suis aussi preneur d'une solution qui me permette carrément de faire des sélect différents de ce type
Tu peux utiliser la syntaxe OPEN FOR et générer une requête dynamiquement :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
SQL> CREATE TABLE t AS
  2  SELECT 1 AS col FROM dual union ALL
  3  SELECT 2        FROM dual union ALL
  4  SELECT 3        FROM dual
  5  /
 
TABLE created.
 
SQL> CREATE OR REPLACE procedure p (p_param IN number, c OUT sys_refcursor)
  2  AS
  3    l_query varchar2(400);
  4  begin
  5    l_query := 'select * from t where 1 = 1 ';
  6    IF p_param = 3 then
  7      l_query := l_query || ' and col in (1,2) ';
  8    else
  9      l_query := l_query || ' and col = 3 ';
 10    end IF;
 11    open c FOR l_query;
 12  end;
 13  /
 
Procedure created.
 
SQL> SHOW err
No errors.
SQL> var rc refcursor
SQL> execute p (3,:rc)
 
PL/SQL procedure successfully completed.
 
SQL> print rc
 
       COL
----------
         1
         2
 
SQL> execute p (1,:rc)
 
PL/SQL procedure successfully completed.
 
SQL> print rc
 
       COL
----------
         3
Mais bon c'est la version rapide sans bind variable, si la nécessité finale est d'exécuter la requête une fois dans un job nocturne c'est valable mais en environnement OLTP, il faut utiliser les binds variables et utiliser une technique plus élaborée comme Varying in lists
Dans ton cas ça pourrait donner :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
SQL> CREATE OR REPLACE context my_ctx USING my_ctx_procedure
  2  /
 
Context created.
 
SQL> CREATE OR REPLACE procedure my_ctx_procedure ( p_str IN varchar2 )
  2  AS
  3  begin
  4    dbms_session.set_context ( 'my_ctx', 'txt', p_str );
  5  end;
  6  /
 
Procedure created.
 
SQL> CREATE OR REPLACE VIEW IN_LIST
  2  AS
  3   SELECT trim( substr (txt,
  4                       instr (txt, ',', 1, level  ) + 1,
  5                       instr (txt, ',', 1, level+1)
  6                     - instr (txt, ',', 1, level) -1 ) ) AS token
  7     FROM (SELECT ','||sys_context('my_ctx','txt')||',' txt
  8            FROM dual)
  9  connect BY level <= length(sys_context('my_ctx','txt'))
 10                             -length(REPLACE(sys_context('my_ctx','txt'),',',''))+1
 11  /
 
VIEW created.
 
SQL> CREATE OR REPLACE procedure p (p_param IN number, c OUT sys_refcursor)
  2  AS
  3  begin
  4    IF p_param = 3 then
  5      my_ctx_procedure( '1,2' );
  6    elsif p_param = 1 then
  7      my_ctx_procedure( '3' );
  8    else
  9      my_ctx_procedure( '1,2,3' );
 10    end IF;
 11    open c FOR SELECT * FROM t WHERE col IN (SELECT * FROM IN_LIST);
 12  end;
 13  /
 
Procedure created.
 
SQL> SHOW err
No errors.
SQL> var rc refcursor
SQL> execute p (3,:rc)
 
PL/SQL procedure successfully completed.
 
SQL> print rc
 
       COL
----------
         1
         2
 
SQL> execute p (1,:rc)
 
PL/SQL procedure successfully completed.
 
SQL> print rc
 
       COL
----------
         3
 
SQL> execute p (8,:rc)
 
PL/SQL procedure successfully completed.
 
SQL> print rc
 
       COL
----------
         1
         2
         3
skuatamad est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/07/2011, 10h25   #3
Membre habitué
 
Inscription : décembre 2004
Messages : 643
Détails du profil
Informations forums :
Inscription : décembre 2004
Messages : 643
Points : 104
Points : 104
Merci pour ta réponse.

1°/Ca confirme qu'on peut donc pas faire avec les CASE

2°/J'avais oublié, mais ça je connais les OPEN FOR, donc ça semble faisable

3°/Je suis désolé mais là je ne comprend absolument rien Je ne reconnais plus du tout l'exemple simple que j'ai donné avec.
-job nocturne : c'est un terme technique? Quel rapport, en fait non ces traitements vont tourner en journée
-j'ai quand-même vaguement essayé de suivre le truc, mais la procédure my_ctx_procedure a des erreurs de compilation à cause du CREATE OR REPLACE context my_ctx USING my_ctx_procedure où j'ai insufficient privileges
-mais où se trouve ma requête dans tout ça?
stof est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/07/2011, 11h48   #4
Membre Expert
 
Inscription : août 2008
Messages : 1 271
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 1 271
Points : 1 929
Points : 1 929
Tu peux peut être aussi te faire une CTE de paramétrage ou -1 correspondrait au ELSE du CASE, quelque chose comme :
Code :
1
2
3
4
5
6
7
8
WITH param (
	SELECT -1 AS c_param, 5 AS val FROM dual union ALL
	SELECT 3            , 120      FROM dual union ALL
	SELECT 3            , 2        FROM dual
)
SELECT t.champ1, t.champ2
  FROM MATABLE t 			
 WHERE t.champ3 IN (SELECT val FROM param WHERE c_param = v_param)
Citation:
Envoyé par stof Voir le message
-job nocturne : c'est un terme technique? Quel rapport, en fait non ces traitements vont tourner en journée
Je me suis mal exprimé, je voulais dire que sans bind variable la requête sera reparsée à chaque fois que la liste des valeurs soumises sera différentes.
Donc si cette requête est fortement utilisée la base parsera inutilement de nombreuses fois la requête.
Si cette requête est assez peu exécutée, c'est moins problématique.
Citation:
Envoyé par stof Voir le message
CREATE OR REPLACE context my_ctx USING my_ctx_procedure où j'ai insufficient privileges
CREATE CONTEXT
Citation:
To create a context namespace, you must have CREATE ANY CONTEXT system privilege.
Citation:
Envoyé par stof Voir le message
-mais où se trouve ma requête dans tout ça?
Dans la procédure P :
Code :
open c FOR SELECT * FROM t WHERE col IN (SELECT * FROM IN_LIST);
skuatamad est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/07/2011, 12h15   #5
Membre éclairé
 
Avatar de boussafi
 
Homme
Ingénieur développement logiciels
Inscription : septembre 2007
Messages : 342
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : Algérie

Informations professionnelles :
Activité : Ingénieur développement logiciels
Secteur : Industrie

Informations forums :
Inscription : septembre 2007
Messages : 342
Points : 397
Points : 397
Envoyer un message via Yahoo à boussafi Envoyer un message via Skype™ à boussafi
Code :
1
2
3
4
SELECT * FROM TABLE t
WHERE t.TABLE LIKE 
decode(&v_param,3,%2%,--si &v_param = 3 alors comprendre 120 et 2
       5)--sinon 5
bon courage
boussafi est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/07/2011, 13h56   #6
Membre habitué
 
Inscription : décembre 2004
Messages : 643
Détails du profil
Informations forums :
Inscription : décembre 2004
Messages : 643
Points : 104
Points : 104
Ah, mais attendez, il y a mieux!!

Code :
1
2
3
4
SELECT t.champ1, t.champ2
FROM MATABLE t 			
WHERE t.champ3 IN 
(SELECT * FROM TABLE(SPLITLST(V_MY_LIST))))
avec :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE OR REPLACE FUNCTION SPLITLST (
	p_list IN VARCHAR2,
	p_del IN VARCHAR2 DEFAULT ','
)
RETURN SPLIT_TABLE_STR pipelined
IS 
    l_idx pls_integer; 
    l_list varchar2(32767) := p_list;
    l_value varchar2(32767);
Begin
    loop 
        l_idx := instr(l_list,p_del);
        IF l_idx > 0 then 
            pipe row(substr(l_list,1,l_idx-1)); 
            l_list := substr(l_list,l_idx+length(p_del));
        else 
        	pipe row(l_list); 
        	exit; 
        end IF; 
    end loop;
RETURN;
End splitlst;
C'est pas mal ça non? J'ai juste à définir ma liste V_MY_LIST en fonction des différents cas '1, 20, 150' dans un cas, '5' dans l'autre, etc.
stof est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/07/2011, 15h47   #7
Membre Expert
 
Inscription : août 2008
Messages : 1 271
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 1 271
Points : 1 929
Points : 1 929
Oui ça fonctionne aussi, c'est la version "If you are in Oracle 8i" du lien "Varying in lists" proposé ci-dessus.
skuatamad est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/07/2011, 16h59   #8
Membre habitué
 
Inscription : décembre 2004
Messages : 643
Détails du profil
Informations forums :
Inscription : décembre 2004
Messages : 643
Points : 104
Points : 104
A peu de choses près oui, c'est vrai

Bon, par contre c'est bien beau de passer '1, 20, 150' en dur, mais c'est autre chose d'aller chercher une liste d'integer dans une table suivant certains critères à la place...
stof est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/07/2011, 21h58   #9
Membre Expert
 
Inscription : août 2008
Messages : 1 271
Détails du profil
Informations forums :
Inscription : août 2008
Messages : 1 271
Points : 1 929
Points : 1 929
Citation:
Envoyé par stof Voir le message
mais c'est autre chose d'aller chercher une liste d'integer dans une table suivant certains critères à la place...
Qu'est ce que tu veux dire par là ?
Si ce sont des id stockés dans une table, il y a forcément moyen de faire plus simple et plus performent que concaténer puis déconcaténer. Donne nous peut être un exemple réaliste de ce que tu veux faire.

Sinon pour concaténer en liste des valeurs d'une colonne, c'est par là :
String Aggregation Techniques
skuatamad est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/07/2011, 10h21   #10
Membre habitué
 
Inscription : décembre 2004
Messages : 643
Détails du profil
Informations forums :
Inscription : décembre 2004
Messages : 643
Points : 104
Points : 104
OK, bon, pour résumer :
-si c'est 2 requêtes différentes suivant les valeurs de "V_PARAM", je fais un OPEN FOR
-si c'est une condition sur une liste dont les valeurs vont dépendre de "V_PARAM", je peux certes faire selon certaines solutions proposées ici mais plus logiquement, il faut surtout faire une table de correspondance COR_champ3 qui ressemble à

Code :
1
2
3
4
5
COR_champ3
V_PARAM    champ3
3               120
3               2
1               5
Code :
1
2
3
4
SELECT t.champ1, t.champ2
FROM MATABLE t 			
WHERE t.champ3 IN 
(SELECT champ3 FROM COR_champ3 WHERE V_PARAM = 3)
stof est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 15h25.


 
 
 
 
Partenaires

Hébergement Web