我需要确定在SQL语句中是否可以将给定字符串解释为数字(整数或浮点)。如下所示:
SELECT AVG(CASE WHEN x ~ '^[0-9]*.?[0-9]*$' THEN x::float ELSE NULL END) FROM test
我发现Postgres'pattern matching可以用于此。所以我调整了this place中给出的语句来合并浮点数。这是我的代码:
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)
如您所见,前两项(空字符串''
和唯一句点'.'
)被错误分类为数字类型(它们不是)。我现在无法接近这一点。任何帮助表示赞赏!
更新根据this answer(及其评论),我将模式改编为:
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)
正如我现在所看到的,科学记数法和负数仍然存在一些问题。
答案 0 :(得分:67)
您可能已经注意到,基于正则表达式的方法几乎不可能正确执行。例如,您的测试表明1.234e-5
不是有效数字,实际上是。此外,你错过了负数。如果某个东西看起来像一个数字,但是当你试图存储它会导致溢出怎么办?
相反,我建议创建一个试图实际转换为NUMERIC
的函数(如果您的任务需要,则为FLOAT
)并返回TRUE
或FALSE
取决于这次演员是否成功。
此代码将完全模拟函数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)
不仅更正确,更容易阅读,如果数据实际上是一个数字,它也会更快。
答案 1 :(得分:11)
您的问题是小数点两边各有两个或多个[0-9]元素。您需要在数字标识行中使用逻辑OR |
:
~'^([0-9]+\.?[0-9]*|\.[0-9]+)$'
这将仅将小数点排除为有效数字。
答案 2 :(得分:-1)
我想可能会有这样的看法(这不是对异常处理的滥用),但是通常我认为应该为此使用异常处理机制。测试字符串是否包含数字是正常处理的一部分,而不是“例外”。
但是您不处理指数是正确的。这是正则表达式(下)的第二个刺。我必须追求使用正则表达式的解决方案的原因是,当遇到错误时给出指令退出时,此处提供为“正确”解决方案的解决方案将失败:
SET exit_on_error = true;
我们通常在运行SQL脚本组时使用它,并且如果有任何问题/错误我们想立即停止时使用。给出此会话指令后,即使没有遇到“真正的”异常,调用isnumeric的“正确”版本也会导致脚本立即退出。
create or replace function isnumeric(text) returns boolean
immutable
language plpgsql
as $$
begin
if $1 is null or rtrim($1)='' then
return false;
else
return (select $1 ~ '^ *[-+]?[0-9]*([.][0-9]+)?[0-9]*(([eE][-+]?)[0-9]+)? *$');
end if;
end;
$$;
答案 3 :(得分:-2)
已接受的解决方案的明显问题是它滥用了异常处理。如果遇到另一个问题,您将永远不会知道,因为您已经抛弃了异常。形式很糟糕。正则表达式将是执行此操作的更好方法。下面的正则表达式似乎运行良好。
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app"></div>
<template id="template">
<div>
<div v-for="item in calculatedItems">
{{ item.item.value }},
Total: {{ item.total }}
</div>
</template>
答案 4 :(得分:-3)
自PostgreSQL 9.5(2016)起,您可以询问json字段的类型:
jsonb_typeof(field)
json_typeof(json)
jsonb_typeof(jsonb)将最外面的JSON值的类型作为文本字符串返回。可能的类型是对象,数组,字符串,数字,布尔值和空。
示例
当聚合数字并希望忽略字符串时:
SELECT m.title, SUM(m.body::numeric)
FROM messages as m
WHERE jsonb_typeof(m.body) = 'number'
GROUP BY m.title;
如果没有::numeric
部分,它将崩溃。