在varchar列中查找非数字值

时间:2017-12-08 09:50:17

标签: sql oracle oracle11g

要求:

通用查询/函数,用于检查表中varchar列中提供的值是否实际为数字&精度不超过允许的精度。

可用值

Table_Name,Column_Name,Allowed Precision,Allowed Scale

一般建议是创建一个功能&使用to_number()来验证值,但是它不会验证允许的长度(精度 - 尺度)。

我的解决方案:

使用Regexp NOT REGEXP_LIKE(COLUMN_NAME, '^-?[0-9.]+$')

验证号码

验证左侧组件的长度(十进制之前)(我不知道它实际上被调用了什么),因为对于缩放,oracle会在需要时自动舍入。由于实际列是varchar,我将使用substr,instr来查找小数点左边的组件。

如上所述Regexp允许数字如123 ... 123124..55我也将验证小数点数。 [如果> 1然后错误]

查询以查找无效的号码

Select * From Table_Name 
Where
(NOT REGEXP_LIKE(COLUMN_NAME, '^-?[0-9.]+$')
OR
Function_To_Fetch_Left_Component(COLUMN_NAME) > (Precision-Scale)
/* Can use regexp_substr now but i already had a function for that */
OR
LENGTH(Column_Name) - LENGTH(REPLACE(Column_Name,'.','')) > 1
/* Can use regexp_count aswell*/)

我很开心&对我的解决方案感到满意,直到只有'。'价值逃过我的支票,我看到了支票的限制。虽然添加另一个检查以验证这一点也可以解决我的问题,solution整体看起来效率非常低。

我会非常感谢[以任何方式]提供更好的解决方案。

提前致谢。

2 个答案:

答案 0 :(得分:2)

寻找:

  • 一个或多个数字,可选地后跟一个小数点和零或多个数字;或
  • 前导小数点(无前一个单位数字),然后是一个或多个(十进制)数字。

像这样:

Select *
From   Table_Name 
Where  NOT REGEXP_LIKE(COLUMN_NAME, '^[+-]?(\d+(\.\d*)?|\.\d+)$')

如果您不想在数字字符串中使用零填充值,则:

Select *
From   Table_Name 
Where  NOT REGEXP_LIKE(COLUMN_NAME, '^[+-]?(([1-9]\d*|0)(\.\d*)?|\.\d+)$')

具有精确度和比例(假设它按照NUMBER( precision, scale )数据类型和scale < precision工作):

Select *
From   Table_Name 
Where  NOT REGEXP_LIKE(COLUMN_NAME, '^[+-]?(\d{1,'||(precision-scale)||'}(\.\d{0,'||scale||'})?|\.\d{1,'||scale||'})$')

或者,对于具有精度和比例的非零填充数字:

Select *
From   Table_Name 
Where  NOT REGEXP_LIKE(COLUMN_NAME, '^[+-]?(([1-9]\d{0,'||(precision-scale-1)||'}|0)(\.\d{0,'||scale||'})?|\.\d{1,'||scale||'})$')

或者,对于任何精度和比例:

Select *
From   Table_Name 
Where  NOT REGEXP_LIKE(
             COLUMN_NAME,
             CASE
               WHEN scale <= 0
               THEN '^[+-]?(\d{1,'||precision||'}0{'||(-scale)||'})$'
               WHEN scale < precision
               THEN '^[+-]?(\d{1,'||(precision-scale)||'}(\.\d{0,'||scale||'})?|\.\d{1,'||scale||'})$'
               WHEN scale >= precision
               THEN '^[+-]?(0(\.0{0,'||scale||'})?|0?\.0{'||(scale-precision)||'}\d{1,'||precision||'})$'
             END
           )

答案 1 :(得分:0)

精度意味着您希望数字中最多allowed_precision个数字(严格来说,不包括前导零,但我会忽略它)。比例意味着最多allowed_scale可以在小数点之后。

这表明正则表达式如:

[-]?[0-9]{1,<before>}[.]?[0-9]{0,<after>}

您可以构造正则表达式:

NOT REGEXP_LIKE(COLUMN_NAME,
                REPLACE(REPLACE('[-]?[0-9]{1,<before>}[.]?[0-9]{0,<after>}', '<before>', allowed_precision - allowed_scale
                               ), '<after>', allowed_scale)

现在,变量正则表达式的效率非常低。您也可以使用like和其他函数来执行逻辑。我认为条件是:

(column_name not like '%.%.%' and
 column_name not like '_%-%' and
 translate(column_name, '0123456789-.x', 'x') is null and
 length(translate(column_name, '-.x', 'x') <= allowed_precision and
 length(translate(column_name, '-.x', 'x') >= 1 and
 instr(translate(column_name, '-.x', 'x'), '.') <= allowed_precision - allowed_scale
)