在转换为数字

时间:2017-02-13 21:43:12

标签: regex postgresql casting numeric

我有这个代码(已经存在,不是我的):

SELECT
    a.id_original_contrato AS contrato,
    ( CASE WHEN d.value~'^\\d+$' THEN d.value::integer ELSE 0 END ) AS monto,
    EXTRACT(YEAR FROM b.value)::integer AS anoinicio,
    EXTRACT(YEAR FROM c.value)::integer AS anofin

......等(一些JOIN和WHERE)

让我解释一下:d.value来自一个值为character varying (200)的表。代码稍后将d.value(现在称为“monto”)作为integer插入到另一个表中。有人为了提取一些字符而编码了正则表达式,或者在其他情况下(ELSE),将其定义为0.这些值仅在integer时起作用。如果我使用像76.44那样的d.value,由于正则表达式不起作用,它总是将其定义为0.。

好吧,我必须更改该代码,因为:

  • 我需要将d.value作为numeric存储在新表中,而不再存储为integer(在我的新表中,数据类型现在为numeric
  • 但首先,我需要更正该正则表达式,因为正在弄乱我的数字数字,如76.4466,56(点或昏迷)。

我不确定正则表达式在做什么。我怎样才能用更好的或新的正则表达式来完成什么需要?

2 个答案:

答案 0 :(得分:2)

选择变体:

with v(value) as (
    values
    ('12,3'),
    ('12.3'),
    ('123'),
    ('123.'),
    ('.123'),
    ('1.2.3')
    )

select 
    value, 
    value ~ '^(\d+[,\.]\d+|\d+)$' as variant_a,
    value ~ '^(\d*[,\.]\d*|\d+)$' as variant_b,
    value ~ '^\d+[,\.]\d+$' as variant_c
from v;

 value | variant_a | variant_b | variant_c 
-------+-----------+-----------+-----------
 12,3  | t         | t         | t
 12.3  | t         | t         | t
 123   | t         | t         | f
 123.  | f         | t         | f
 .123  | f         | t         | f
 1.2.3 | f         | f         | f
(6 rows)

要将带点或逗号的字符串转换为数字,请使用replace()

select replace(value, ',', '.')::numeric;   

答案 1 :(得分:2)

您应该声明Postgres的版本以及编写代码时使用的版本(如果您知道的话)。 \\d中的双反斜杠表示旧版本standard_conforming_strings = offThe manual:

  

从PostgreSQL 9.1开始,默认值为on(之前的版本默认为关闭)。

standard_conforming_strings = on的现代版本中,此字符串作为正则表达式毫无意义: '^\\d+$' 。要检测由一个或多个数字组成的字符串,请使用E'^\\d+$'(前缀为E)或'^\d+$'。详细说明:

整数文字还允许可选的前导符号表示负数/正数。在Postgres中也允许(自动修剪)前导/悬空空格 所以,这是integer的完整正则表达式:

CASE WHEN d.value ~ '^\s*[-+]?\d+\s*$' THEN d.value::int ELSE 0 END

正则表达式解释说:

^ ..开头的字符串
\s class shorthand [[:space:]](空格)
* .. quantifier 0次或更多次 [+-] ..由+-组成的字符类 ? ..量词为0或1次
\d .. [[:digit:]](数字)的简写类 + ..量词为1次或更多次 \s* ..与上述相同 $ ..字符串结尾

现在我们了解基础知识。阅读我链接到的手册中的更多内容。考虑numeric string literals的语法规则。而且,关于合法数字常数的状态:

  

常量

中不能嵌入任何空格或其他字符

那是因为没有引用数字常量,因此无法使用空格。不适用于投射字符串。空白容忍: 在指数char之后的前导,尾随和右边。

所以这些都是演员numeric的合法字符串:

<强> '^\s*[-+]?\d*\.?\d+(?:[eE]\s*[-+]?\d+)?\s*$'

唯一的新元素是parentheses (()) to denote the contained regular expression as atom。由于我们对反向引用不感兴趣,请使用“非捕获”:(?: ... )并附加问号(?:[eE]\s*[-+]?\d+)?表示:可以添加“指数”部分或不,作为一个整体

假设点(.)为小数点分隔符。您可以使用逗号(,)或[,\.]来允许。但是只有点才对演员合法。

测试:

SELECT '|' || txt || '|' As text_with_delim
     , txt ~ '^\s*[-+]?\d*\.?\d+([eE]\s*[-+]?\d+)?\s*$' As test
     , txt::numeric AS number
FROM   unnest ('{1, 123, 000, "  -1     ", +2, 1.2, .34, 5e6, " .5e   -6  "}'::text[]) txt;

结果:

 text_with_delim | test |  number
-----------------+------+-----------
 |1|             | t    |         1
 |123|           | t    |       123
 |000|           | t    |         0
 |  -1     |     | t    |        -1
 |+2|            | t    |         2
 |1.2|           | t    |       1.2
 |.34|           | t    |      0.34
 |5e6|           | t    |   5000000
 | .5e   -6  |   | t    | 0.0000005

或者您可以使用to_number()转换任意给定格式的字符串。