【问题标题】:Invalid numbers无效号码
【发布时间】:2014-08-09 01:32:01
【问题描述】:

所以,我有一列数据,使用前面的示例,体温,它存储为 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 字段中的数据,这些是唯一的字符响应。

所有其他响应,无论是否合法临时,都是数字,除了小数之外没有其他字符。

我是否需要在括号或其他内容中链接“不喜欢”语句?

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

【问题讨论】:

  • 您提到“所有其他响应,无论是否合法临时,都是数字,除了小数之外没有其他字符。”。所以,cast(result_num as integer) 预计会返回小数错误,对吧?
  • 小数应该没问题,我在不同的查询上使用了类似的命令,小数工作正常。
  • 如果您的会话和所有插入使用相同的 NLS_NUMERIC_CHARACTERS 设置,小数应该没问题。您可以使用something like this 来查找它所触发的值;一旦你知道它不喜欢什么,就会更清楚为什么。另请注意(again),它可能不是导致错误的TEMP 记录,它取决于谓词的评估顺序,因此请检查all result_num 值。
  • 因此,如果我在表格中有其他生命体征结果,例如可能小于 8 的 HbA1c,我需要在其中为所有其他特殊字符实例添加“不喜欢”行也一样?

标签: sql oracle ora-01722


【解决方案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】:

    编辑: 如果您使用的是 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

    【讨论】:

    • 我看到这是同一个命令,只是格式不同。这真的能有所作为吗?现在就试试。
    • @user3748543 请参阅上面的修订答案。
    【解决方案3】:

    您可以使用 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.

    【讨论】:

      猜你喜欢
      • 2019-08-09
      • 1970-01-01
      • 2023-03-15
      • 2020-05-09
      • 1970-01-01
      • 1970-01-01
      • 2022-01-12
      • 1970-01-01
      相关资源
      最近更新 更多