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

Posted

技术标签:

【中文标题】条件 Where 子句比较数字值(如果它是数字)或字符串值(如果它不是数字)【英文标题】:Conditional Where clause comparing the number value if it's a number or the string value if it's not a number 【发布时间】:2016-06-09 14:47:08 【问题描述】:

我有一个列字段,它是 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: invalid number,因为 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】:

您可以通过创建几个函数来确定该值是否为数字来做到这一点:

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() 的功能所付出的代价。您必须手动添加缺失的案例。

【讨论】:

遗憾的是,我不允许向数据库添加功能,我只能通过查询来处理它。 如果您使用的是 12c(我不是),您可以将上述函数直接添加到 sql 中,这可能会绕过约束。否则,我已更新我的答案以包含基于正则表达式的解决方案。

以上是关于条件 Where 子句比较数字值(如果它是数字)或字符串值(如果它不是数字)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 laravel 中查询只有字母数字值的行?

ORA-01722: 无效的数字 where 子句

在mysql select语句中使用条件where子句

关于sql语句添加where条件问题,用java语句

如果where子句在SQL中有重复的条件值,如何获取多行

如果满足 COUNT 条件,我可以应用 WHERE 子句吗?