Sql 无效数字

Sql 无效数字,sql,oracle,ora-01722,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

所以,我有一列数据,使用前面的例子,体温,它存储为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中的“不喜欢”语句链接起来吗

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

编辑: 如果您使用的是Oracle 10g之后的版本,则可以使用正则表达式在结果_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;

参考


您可以使用或使用正则表达式筛选出非数值-这可能需要一些调整:

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;

这将排除大多数非数字(可能全部,但我不太自信-正则表达式不是一个强大的领域),尽管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;

这对我来说很有效。这本质上是@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上,或者如果虚拟列听起来像是太多炼金术,另一种方法是制作一个普通的旧列来保存经过消毒的值,并让触发器在插入/更新时计算其值。

您提到“所有其他响应,无论是否合法,都是数字,除小数外没有其他字符。”。所以,cast(result_num as integer)应该返回小数错误,对吗?小数应该可以,我在不同的查询中使用了类似的命令,小数工作正常。如果会话和所有插入使用相同的NLS_数字字符设置,小数应该可以。您可以使用来查找它所遇到的值;一旦你知道它不喜欢什么,你就会明白为什么。还要注意的是,可能不是
TEMP
记录导致了错误,这取决于谓词的评估顺序,因此请检查所有
result\u num
值。因此,如果表中有其他重要指标结果,比如可能是HbA1c,我看到这是相同的命令,只是格式不同。这真的能改变吗?现在就试试。@user3748543请看上面修改的答案。