Sql 无效数字
所以,我有一列数据,使用前面的例子,体温,它存储为varchar,这样就不会拒绝任何记录,但是,它包含数字数据 发送数据的人使用的系统不够完善,所以我有一些不正确的数据。我需要做的是编写一个SQL查询来查找高于或低于某个值的有效值 例如,所有温度超过104,这应表明极端情况或错误 我试过: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
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请看上面修改的答案。