与PostgreSQL的isnumeric()

moo*_*eep 34 regex postgresql

我需要确定一个给定的字符串是否可以解释为SQL语句中的数字(整数或浮点).如下所示:

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

我发现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;
Run Code Online (Sandbox Code Playgroud)

输出:

    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)
Run Code Online (Sandbox Code Playgroud)

如您所见,前两项(空字符串''和唯一句点'.')被错误分类为数字类型(它们不是).我现在无法接近这一点.任何帮助赞赏!


更新根据这个答案(及其评论),我将模式调整为:

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;
Run Code Online (Sandbox Code Playgroud)

这使:

     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)
Run Code Online (Sandbox Code Playgroud)

正如我现在所看到的,科学记数法和负数仍然存在一些问题.

mvp*_*mvp 74

您可能已经注意到,基于正则表达式的方法几乎不可能正确执行.例如,您的测试表明这1.234e-5不是有效数字,当它确实存在时.此外,你错过了负数.如果某个东西看起来像一个数字,但是当你试图存储它会导致溢出怎么办?

相反,我建议创建试图实际转换为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;
Run Code Online (Sandbox Code Playgroud)

在您的数据上调用此函数会得到以下结果:

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)
Run Code Online (Sandbox Code Playgroud)

它不仅更正确,更容易阅读,如果数据实际上是一个数字,它也会更快.

  • 好吧,我的观点是,如果你试图说如果存储在Postgres数据库中的给定字符串是一个有效的数字,唯一合理的方法是询问Postgres服务器本身对它的看法.如果它说`1.234d + 5`不是有效数字,那么你无法使用Postgres方法将其强制转换为有效数字. (3认同)
  • 我已经编辑了答案以包含上面讨论的“STRICT”关键字。这是一个数据库函数;它应该用“NULL”做正确的事情。我测试了它,如果没有“STRICT”,它会为“NULL”返回“true”,这绝不是任何人想要的。 (3认同)
  • 有一个更简单的解决方案来处理`NULL`s.保持函数体不变,只需将此行添加到函数声明:[`RETURNS NULL ON NULL INPUT`](http://www.postgresql.org/docs/9.1/static/sql-createfunction.html) (2认同)
  • 再次重新阅读文档之后,事实证明只需添加关键字`STRICT`就相当于`RETURNS NULL ON NULL INPUT`并做你想要的. (2认同)

Mr *_*Rho 11

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

~'^([0-9]+\.?[0-9]*|\.[0-9]+)$'
Run Code Online (Sandbox Code Playgroud)

这将仅将小数点排除为有效数字.

  • 你错过了```的一些逃脱,它们将匹配''1x1'和''x1'. (4认同)