条件Where子句比较数字值,如果它是数字或字符串值,如果它不是数字

时间:2016-06-09 14:47:09

标签: sql oracle numbers conditional

我的列字段是varchar,并且包含数字和字符条目。

MyColumn将包含这些值,例如:

  

" 1.0"," AB"," A1"," 1"," 100"

如果用户键入" 1"在我的应用领域,我想做一个varchar搜索,匹配" 1"还有一个" 1.0"。

我无法做一个TO_NUMBER,因为有些数据不是数字,我会得到一个"而不是数字"异常。

我尝试使用OR子句检查myColumn是否为数字,然后再进行强制转换:

(trim(TRANSLATE(myColumn,'0123456789', ' ')) is null and TO_NUMBER(myColumn) = 1.0) 
or myColumn = '1.0' 

但我仍然得到ORA-01722:无效的数字,因为Oracle不会在另一方之前检查AND子句的一侧。

我试图"大概是"限制的两面:

case when trim(TRANSLATE(myColumn,'0123456789', ' ')) is null then TO_NUMBER(myColumn) else myColumn end 
= case when trim(TRANSLATE(myColumn,'0123456789', ' ')) is null then 1.0 else '1.0' end;

但是我得到了ORA-00932不一致的数据类型

没有办法在oracle中执行条件where子句吗?

1 个答案:

答案 0 :(得分:0)

您可以通过创建几个函数来确定值是否为数字来执行此操作:

create or replace function is_number (p_value varchar2)
return number
is
begin
  return to_number(p_value);
exception
  when value_error then
    return null;
end;
/

create or replace function is_not_number (p_value varchar2)
return varchar2
is
  v_num number;
begin
  v_num := is_number(p_value);
  return case when v_num is null then p_value
         end;
end;
/

然后在适当的过滤谓词中使用这些函数:

with sample_data as (select '1.0' val from dual union all
                     select 'AB' val from dual union all
                     select 'A1' val from dual union all
                     select '1' val from dual union all
                     select '100' val from dual)
select val
from   sample_data
where  is_number(val) = 1;

VAL
---
1.0
1  

with sample_data as (select '1.0' val from dual union all
                     select 'AB' val from dual union all
                     select 'A1' val from dual union all
                     select '1' val from dual union all
                     select '100' val from dual)
select val
from   sample_data
where  is_not_number(val) = 'AB';

VAL
---
AB 

它不会非常高效,但是如果您要将任何旧数据推送到同一列中,那么您就不会充分利用数据库。

如果您不允许添加功能或更改基础数据结构(boo * {:-()),则可能会使用以下内容:

with sample_data as (select '1.0' val from dual union all
                     select 'AB' val from dual union all
                     select 'A1' val from dual union all
                     select '1' val from dual union all
                     select '100' val from dual union all
                     select '.100' val from dual union all
                     select '1e-10' val from dual union all
                     select '2.3e5' val from dual union all
                     select '-10' val from dual union all
                     select '+10' val from dual union all
                     select '+91.3e+2' val from dual union all
                     select '+-9.3e-2' val from dual union all
                     select '.1E5' val from dual union all
                     select '1.' val from dual union all
                     select '1.1.1' val from dual)
select val,
       case when regexp_like(trim(val), '^(-|\+)?'|| -- checks for the presence of a positive or negative sign. This applies to all subsequent checks
                                                 '([[:digit:]]+(\.[[:digit:]]*$|$)'|| -- matches bog standard numbers with or without a decimal part (eg. 1, 1.01, 1.)
                                                 '|\.{1}[[:digit:]]+$'|| -- matches a number that has no digits before the decimal point (eg. .1)
                                                 '|[[:digit:]]+\.?[[:digit:]]*(e|E)(-|\+)?[[:digit:]]+$'|| -- matches scientific numbers starting with an integer with positive or negative numbers after the e/E (eg. 1e10, 1e-10)
                                                 '|\.[[:digit:]]+(e|E)(-|\+)?[[:digit:]]+$)' -- matches scientific numbers with starting with a decimal point  (eg. .1e10, .1e-10)
                             ) then to_number(val) 
       end num,
       case when not regexp_like(trim(val), '^(-|\+)'||
                                                    '?([[:digit:]]+(\.[[:digit:]]*$|$)'||
                                                    '|\.{1}[[:digit:]]+$'||
                                                    '|[[:digit:]]+\.?[[:digit:]]*(e|E)(-|\+)?[[:digit:]]+$'||
                                                    '|\.[[:digit:]]+(e|E)(-|\+)?[[:digit:]]+$)') then val
       end txt
from   sample_data;

VAL             NUM TXT     
-------- ---------- --------
1.0               1         
AB                  AB      
A1                  A1      
1                 1         
100             100         
.100             .1         
1e-10        1.E-10         
2.3e5        230000         
-10             -10         
+10              10         
+91.3e+2       9130         
+-9.3e-2            +-9.3e-2
.1e5          10000         
1.                1         
1.1.1               1.1.1   

请注意,我可能错过了某些值为有效数字的情况;这是您必须手动复制to_number()功能所需的价格。您必须手动添加丢失的案例。