isnumeric()和PostgreSQL

我需要确定给定的string是否可以在SQL语句中被解释为数字(整数或浮点数)。 如下所示:

SELECT AVG(CASE WHEN x ~ '^[0-9]*.?[0-9]*$' THEN x::float ELSE NULL END) FROM test 

我发现Postgres的模式匹配可以用于这个。 所以我改编了这个地方给出的语句来结合浮点数。 这是我的代码:

 WITH test(x) AS ( VALUES (''), ('.'), ('.0'), ('0.'), ('0'), ('1'), ('123'), ('123.456'), ('abc'), ('1..2'), ('1.2.3.4')) SELECT x , x ~ '^[0-9]*.?[0-9]*$' AS isnumeric FROM test; 

输出:

  x | isnumeric ---------+----------- | t . | t .0 | t 0. | t 0 | t 1 | t 123 | t 123.456 | t abc | f 1..2 | f 1.2.3.4 | f (11 rows) 

正如你所看到的,前两个项目(空string''和唯一句点'.' )被错误分类为数字types(它们不是)。 目前我无法接近这一点。 任何帮助感激!


更新基于这个答案 (及其评论),我调整了这个模式:

 WITH test(x) AS ( VALUES (''), ('.'), ('.0'), ('0.'), ('0'), ('1'), ('123'), ('123.456'), ('abc'), ('1..2'), ('1.2.3.4'), ('1x234'), ('1.234e-5')) SELECT x , x ~ '^([0-9]+[.]?[0-9]*|[.][0-9]+)$' AS isnumeric FROM test; 

这使:

  x | isnumeric ----------+----------- | f . | f .0 | t 0. | t 0 | t 1 | t 123 | t 123.456 | t abc | f 1..2 | f 1.2.3.4 | f 1x234 | f 1.234e-5 | f (13 rows) 

正如我现在所看到的那样,科学记数法还有一些问题,并带有负数。

正如您可能已经注意到的那样,基于正则expression式的方法几乎不可能正确执行。 例如,你的testing说, 1.234e-5是无效的数字,当它真的是。 另外,你错过了负数。 如果东西看起来像一个数字,但是当你试图存储它会导致溢出?

相反,我会build议创build函数,试图实际转换为NUMERIC (或FLOAT如果您的任务需要它),并返回TRUEFALSE取决于这个演员是否成功。

这段代码将完全模拟函数ISNUMERIC()

 CREATE OR REPLACE FUNCTION isnumeric(text) RETURNS BOOLEAN AS $$ DECLARE x NUMERIC; BEGIN x = $1::NUMERIC; RETURN TRUE; EXCEPTION WHEN others THEN RETURN FALSE; END; $$ STRICT LANGUAGE plpgsql IMMUTABLE; 

对数据调用这个函数得到如下结果:

 WITH test(x) AS ( VALUES (''), ('.'), ('.0'), ('0.'), ('0'), ('1'), ('123'), ('123.456'), ('abc'), ('1..2'), ('1.2.3.4'), ('1x234'), ('1.234e-5')) SELECT x, isnumeric(x) FROM test; x | isnumeric ----------+----------- | f . | f .0 | t 0. | t 0 | t 1 | t 123 | t 123.456 | t abc | f 1..2 | f 1.2.3.4 | f 1x234 | f 1.234e-5 | t (13 rows) 

不仅更正确,更容易阅读,而且数据实际上是一个数字,它也会更快。

你的问题是在小数点的每一边有两个0或更多的[0-9]元素。 您需要使用逻辑OR | 在号码标识行中:

 ~'^([0-9]+\.?[0-9]*|\.[0-9]+)$' 

这将排除一个小数点作为一个有效的数字。