使用CASE和IN更新 - Oracle

时间:2011-03-02 17:06:21

标签: sql oracle sql-update

我写了一个像SQL Server中的魅力一样的查询。不幸的是,它需要在Oracle数据库上运行。我一直在网上搜索有关如何转换它的解决方案,但没有任何成功:/

查询看起来像我的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`

我的问题还在于列budgetpost_gr1和budgetpost是字母数字,而Oracle似乎希望将列表视为数字。该列表是预定义为逗号分隔列表的变量/参数,它们只是转储到查询中。

5 个答案:

答案 0 :(得分:33)

你说预算邮件是字母数字。这意味着它正在寻找与字符串的比较。您应该尝试将参数括在单引号中(并且您在Case表达式中缺少最终的THEN。)

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 

答案 1 :(得分:1)

使用to_numberbudgpost转换为数字:

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

编辑:确保9中有足够to_number个符合最大预算的帖子。

如果有非数字预算帖子,您可以在查询结尾处使用where子句对其进行过滤:

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

答案 2 :(得分:1)

“列表是预定义为逗号分隔列表的变量/参数”。 你的意思是你的查询实际上是

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`

如果是这样,你需要一个函数来获取一个字符串并将其解析为一个数字列表。

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;
/

然后您可以使用如下函数:

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'

答案 3 :(得分:1)

有一个运行的解决方案。不知道它是否是最佳的。我所做的是根据http://blogs.oracle.com/aramamoo/2010/05/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement.html

分割字符串

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

所以我的最终代码如下所示($bp_gr1'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;

有没有办法让它跑得更快?

答案 4 :(得分:0)

您可以使用另一种解决方法来使用联接进行更新。下面的示例假定您希望通过包含查找值(在这种情况下在表中存储用户名)来对表进行反规范化。此更新包括一个用于查找名称的连接,并在CASE语句中评估输出,该语句支持找到或未找到的名称。实现这一目标的关键是确保从连接中出来的所有列都具有唯一的名称。在示例代码中,请注意b.user_name如何与a.user_name列冲突,并且必须使用唯一名称“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;