数字无效

时间:2014-06-18 16:39:40

标签: sql oracle ora-01722

所以,我有一列数据,使用前面的例子,体温,存储为varchar,以便没有记录被拒绝,但是,它包含数字数据。

发送数据的人使用的系统不完美,所以我的数据不正确。我需要做的是编写一个SQL查询来查找高于或低于某个值的有效值。

例如,所有临时值都超过104,这应该表示极端情况或错误。

我试过了:

select count(1), result_num from VITALS where test_cd is 'TEMP' and cast(result_num as integer) > 104 group by result_num;

这返回了一个无效的数字错误,所以我想我的某些行上的字符无法转换为整数,我发现了带负值的记录(数字前面的“ - ”)和一些说“NULL”的记录,所以我将查询修改为:

select count(1), result_num from VITALS where test_cd is 'TEMP' **and result_num not like '%-%' and result_num not like '%NULL%'** and cast(result_num as integer) > 104 group by result_num;

...它仍然返回无效的数字错误。我已经三次检查了我的RESULT_NUM字段中的数据,这些是唯一的字符响应。

所有其他回复,无论是否合法,都是数字,没有小数以外的字符。

我是否需要在parens或其他内容中链接“not like”语句?

这可能是一个简单的答案,但它让我疯狂。

3 个答案:

答案 0 :(得分:1)

您可以使用a function like this answer provides或正则表达式过滤掉非数字值 - 这可能需要进行一些调整:

select count(1), result_num
from vitals
where test_cd = 'TEMP'
and regexp_like(result_num, '^[-]?[0-9]*[\.]?[0-9]*$')
and cast(result_num as integer) > 104
group by result_num;

SQL Fiddle

这将排除大多数非数字(可能全部,但我不那么自信 - 正则表达式不是一个强大的领域),尽管Justin的功能可能更安全。

但是,仍无法保证在演员表演之前应用滤镜功能。如果这仍然绊倒,那么您可以使用子查询过滤掉非数字值,然后检查剩余的值的实际值;但您可能需要添加一个提示来阻止Oracle取消子查询并更改您的评估顺序。

另一种方法是Justin函数的变体,它返回实际数字:

CREATE OR REPLACE FUNCTION safe_number( p_str IN VARCHAR2 )
  RETURN NUMBER DETERMINISTIC PARALLEL_ENABLE
IS
  l_num NUMBER;
BEGIN
  l_num := to_number( p_str );
  RETURN l_num;
EXCEPTION
  WHEN value_error THEN
    RETURN null;
END safe_number;
/

然后您的查询可以使用:

select count(1), result_num
from vitals
where test_cd = 'TEMP'
and safe_number(result_num) > 104
group by result_num;

SQL Fiddle

答案 1 :(得分:1)

这对我有用。它基本上是@Alex对子查询做出的建议。希望它适用于您的源数据:

SELECT count(*), result_num
FROM
(
  SELECT
  test_cd,
  CASE WHEN REGEXP_LIKE(result_num,'^-?[0-9]*\.?[0-9]*$')
    THEN result_num - 0
    ELSE NULL
  END result_num
  FROM vitals
) 
WHERE test_cd='TEMP'
  AND result_num > 104
GROUP BY result_num;

另一个不同的想法:如果您使用的是Oracle 11g,并且您有能力建议/进行表格结构更改,我可能会想要添加虚拟列(即计算列的计算列,用于计算已清理的数值。行为类似于视图,但通过使用虚拟列有两大优势:(1)没有其他对象,仍然是一个对象,vitals表,以及(2)您可以在虚拟列上编制索引(逻辑上与功能索引相同)。

如果您没有使用11g,或者虚拟列听起来太多炼金术,另一种方法是制作一个普通的旧列来保存已清理的值,并在插入/更新时触发计算其值。

答案 2 :(得分:0)

修改: 如果您使用的是Oracle 10g之后的版本,则可以使用正则表达式在result_num中仅查找数字字符。由于要过滤掉小数和负数,您只需查找字符0-9,如下所示:

select result_num , count(1) as cnt_result_num
from VITALS 
where test_cd = 'TEMP' 
and result_num IS NOT NULL
and regexp_like(result_num, '^[0-9]*$')
and cast(result_num as integer) > 104 
group by result_num;

SQL Fiddle

<强>参考

REGEXP_LIKE on Oracle Database SQL Reference