web-dev-qa-db-fra.com

MISE À JOUR avec CASE et IN - Oracle

J'ai écrit une requête qui fonctionne comme un charme dans SQL Server. Malheureusement, il doit être exécuté sur une base de données Oracle. J'ai cherché sur le Web à l'envers pour trouver une solution sur la façon de le convertir, sans succès: /

La requête ressemble à ceci dans SQL:

UPDATE tab1   SET budgpost_gr1=     
CASE  WHEN (budgpost in (1001,1012,50055))  THEN 'BP_GR_A'   
      WHEN (budgpost in (5,10,98,0))  THEN 'BP_GR_B'  
      WHEN (budgpost in (11,876,7976,67465))     
      ELSE 'Missing' END`

Mon problème est également que les colonnes budgetpost_gr1 et budgetpost est alphanumérique et Oracle semble vouloir voir la liste sous forme de nombres. La liste est constituée de variables/paramètres prédéfinis en tant que listes séparées par des virgules, qui sont simplement transférées dans la requête.

23
user641605

Vous avez une solution qui fonctionne. Je ne sais pas si c'est optimal. Ce que je fais est de diviser la chaîne en fonction de http://blogs.Oracle.com/aramamoo/2010/05/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement.html

En utilisant:
select regexp_substr(' 1, 2 , 3 ','[^,]+', 1, level) from dual
connect by regexp_substr('1 , 2 , 3 ', '[^,]+', 1, level) is not null;

Donc, mon code final ressemble à ceci ($bp_gr1' sont des chaînes comme 1,2,3):

UPDATE TAB1
SET    BUDGPOST_GR1 =
          CASE
             WHEN ( BUDGPOST IN (SELECT     REGEXP_SUBSTR ( '$BP_GR1',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                 FROM       DUAL
                                 CONNECT BY REGEXP_SUBSTR ( '$BP_GR1',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                               IS NOT NULL) )
             THEN
                'BP_GR1'
             WHEN ( BUDGPOST IN (SELECT     REGEXP_SUBSTR ( ' $BP_GR2',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                 FROM       DUAL
                                 CONNECT BY REGEXP_SUBSTR ( '$BP_GR2',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                               IS NOT NULL) )
             THEN
                'BP_GR2'
             WHEN ( BUDGPOST IN (SELECT     REGEXP_SUBSTR ( ' $BP_GR3',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                 FROM       DUAL
                                 CONNECT BY REGEXP_SUBSTR ( '$BP_GR3',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                               IS NOT NULL) )
             THEN
                'BP_GR3'
             WHEN ( BUDGPOST IN (SELECT     REGEXP_SUBSTR ( '$BP_GR4',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                 FROM       DUAL
                                 CONNECT BY REGEXP_SUBSTR ( '$BP_GR4',
                                                            '[^,]+',
                                                            1,
                                                            LEVEL )
                                               IS NOT NULL) )
             THEN
                'BP_GR4'
             ELSE
                'SAKNAR BUDGETGRUPP'
          END;

Existe-t-il un moyen de le faire fonctionner plus rapidement?

1
user641605

Vous avez dit que budgetpost est alphanumérique. Cela signifie qu'il recherche des comparaisons avec des chaînes. Vous devriez essayer de placer vos paramètres entre guillemets simples (et il vous manque le THEN final dans l'expression Case).

UPDATE tab1   
SET budgpost_gr1=   CASE  
                        WHEN (budgpost in ('1001','1012','50055'))  THEN 'BP_GR_A'   
                        WHEN (budgpost in ('5','10','98','0'))  THEN 'BP_GR_B'  
                        WHEN (budgpost in ('11','876','7976','67465')) THEN 'What?'
                        ELSE 'Missing' 
                        END 
33
Thomas

Utilisation to_number pour convertir budgpost en un nombre:

when to_number(budgpost,99999) in (1001,1012,50055) THEN 'BP_GR_A' 

EDIT: assurez-vous qu'il y a suffisamment de 9'péché to_number pour correspondre au poste budgétaire le plus important.

S'il existe des postes budgétaires non numériques, vous pouvez les filtrer avec une clause where à la fin de la requête:

where regexp_like(budgpost, '^-?[[:digit:],.]+$')
1
Andomar

Msgstr "La liste est des variables/paramètres prédéfinis comme des listes séparées par des virgules". Voulez-vous dire que votre requête est en fait

UPDATE tab1   SET budgpost_gr1=     
CASE  WHEN (budgpost in ('1001,1012,50055'))  THEN 'BP_GR_A'   
      WHEN (budgpost in ('5,10,98,0'))  THEN 'BP_GR_B'  
      WHEN (budgpost in ('11,876,7976,67465'))     
      ELSE 'Missing' END`

Si c'est le cas, vous avez besoin d'une fonction pour prendre une chaîne et l'analyser dans une liste de nombres.

create type tab_num is table of number;

create or replace function f_str_to_nums (i_str in varchar2) return tab_num is
  v_tab_num tab_num := tab_num();
  v_start   number := 1;
  v_end     number;
  v_delim   VARCHAR2(1) := ',';
  v_cnt     number(1) := 1;
begin
  v_end := instr(i_str||v_delim,v_delim,1, v_start);
  WHILE v_end > 0 LOOP
    v_cnt := v_cnt + 1;
    v_tab_num.extend;
    v_tab_num(v_tab_num.count) := 
                  substr(i_str,v_start,v_end-v_start);
    v_start := v_end + 1;
    v_end := instr(i_str||v_delim,v_delim,v_start);
  END LOOP;
  RETURN v_tab_num;
end;
/

Ensuite, vous pouvez utiliser la fonction comme suit:

select column_id, 
   case when column_id in 
     (select column_value from table(f_str_to_nums('1,2,3,4'))) then 'red' 
   else 'blue' end
from  user_tab_columns
where table_name = 'EMP'
1
Gary Myers

Il existe une autre solution de contournement que vous pouvez utiliser pour mettre à jour à l'aide d'une jointure. Cet exemple ci-dessous suppose que vous souhaitez dénormaliser une table en incluant une valeur de recherche (dans ce cas, stocker un nom d'utilisateur dans la table). La mise à jour inclut une jointure pour rechercher le nom et la sortie est évaluée dans une instruction CASE qui prend en charge le nom trouvé ou introuvable. La clé pour que cela fonctionne est de s'assurer que toutes les colonnes sortant de la jointure ont des noms uniques. Dans l'exemple de code, remarquez comment b.user_name entre en conflit avec la colonne a.user_name et doit être aliasé avec le nom unique "user_user_name".

UPDATE
(
    SELECT a.user_id, a.user_name, b.user_name as user_user_name
    FROM some_table a
    LEFT OUTER JOIN user_table b ON a.user_id = b.user_id
    WHERE a.user_id IS NOT NULL
)
SET user_name = CASE
    WHEN user_user_name IS NOT NULL THEN user_user_name
    ELSE 'UNKNOWN'
    END;   
0
Scott