如何在使用PostgreSQL进行转换时将字符串转换为整数并且为0时出现错误?

sil*_*iot 117 sql postgresql casting

在PostgreSQL中,我有一个带varchar列的表.数据应该是整数,我在查询中需要整数类型.有些值是空字符串.下列:

SELECT myfield::integer FROM mytable
Run Code Online (Sandbox Code Playgroud)

产量 ERROR: invalid input syntax for integer: ""

如何在postgres中投射期间查询演员并且在出现错误时为0?

Ant*_*ggs 151

我自己只是在解决类似的问题,但不想要一个函数的开销.我想出了以下查询:

SELECT myfield::integer FROM mytable WHERE myfield ~ E'^\\d+$';
Run Code Online (Sandbox Code Playgroud)

Postgres会快速调整其条件,因此你不应该让任何非整数命中你的::整数.它还处理NULL值(它们与regexp不匹配).

如果你想要零而不是不选择,那么CASE语句应该有效:

SELECT CASE WHEN myfield~E'^\\d+$' THEN myfield::integer ELSE 0 END FROM mytable;
Run Code Online (Sandbox Code Playgroud)

  • 我强烈建议按照马修的建议去做.此解决方案存在字符串问题,这些字符串看起来像数字但大于您可以放在整数中的最大值. (14认同)
  • 我第二次发表评论.最大值是等待发生的错误.不抛出错误的一点是在数据无效时不抛出错误.这个接受的答案并没有解决这个问题 谢谢马修!做得好! (4认同)
  • 和马修的答案一样,我只需要一种快速而肮脏的处理方法来检查一些数据.我也承认,在SQL中定义函数时,我现在缺乏自己的知识.我只对1到5位数之间的数字感兴趣,因此我将正则表达式更改为"E"\\ d {1,5} $'`. (3认同)
  • 是的,是的,这个解决方案相对快速和肮脏,但在我的情况下,我知道我有什么数据,表格相对较短.它比编写(和调试)整个函数容易得多.如果你担心溢出,@ Bobort对数字的"{1,5}"限制可能是一个好主意,但它会掩盖更大的数字,如果你转换表格可能会造成麻烦.就个人而言,我宁愿预先查询错误并知道我的一些"整数"是搞乱的(你也可以选择`E'\\ d {6,} $'`来确保). (3认同)
  • @StefanSteiger 我不明白这有什么问题。像这样的字段如何通过正则表达式? (3认同)

Mat*_*ood 89

您还可以创建自己的转换函数,在其中可以使用异常块:

CREATE OR REPLACE FUNCTION convert_to_integer(v_input text)
RETURNS INTEGER AS $$
DECLARE v_int_value INTEGER DEFAULT NULL;
BEGIN
    BEGIN
        v_int_value := v_input::INTEGER;
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Invalid integer value: "%".  Returning NULL.', v_input;
        RETURN NULL;
    END;
RETURN v_int_value;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

测试:

=# select convert_to_integer('1234');
 convert_to_integer 
--------------------
               1234
(1 row)

=# select convert_to_integer('');
NOTICE:  Invalid integer value: "".  Returning NULL.
 convert_to_integer 
--------------------

(1 row)

=# select convert_to_integer('chicken');
NOTICE:  Invalid integer value: "chicken".  Returning NULL.
 convert_to_integer 
--------------------

(1 row)
Run Code Online (Sandbox Code Playgroud)

  • 与已接受的答案相反,这里的解决方案更正确,因为它同样可以很好地处理数字太大而不适合整数,并且它也可能更快,因为它在常见情况下没有验证工作(=有效字符串) ) (6认同)

ghb*_*att 26

我有同样的需求,发现这对我有用(postgres 8.4):

CAST((COALESCE(myfield,'0')) AS INTEGER)
Run Code Online (Sandbox Code Playgroud)

一些测试用例来演示:

db=> select CAST((COALESCE(NULL,'0')) AS INTEGER);
 int4
------
    0
(1 row)

db=> select CAST((COALESCE('','0')) AS INTEGER);
 int4
------
    0
(1 row)

db=> select CAST((COALESCE('4','0')) AS INTEGER);
 int4
------
    4
(1 row)

db=> select CAST((COALESCE('bad','0')) AS INTEGER);
ERROR:  invalid input syntax for integer: "bad"
Run Code Online (Sandbox Code Playgroud)

如果需要处理字段具有非数字文本的可能性(例如"100bad"),则可以使用regexp_replace在强制转换之前删除非数字字符.

CAST(REGEXP_REPLACE(COALESCE(myfield,'0'), '[^0-9]+', '', 'g') AS INTEGER)
Run Code Online (Sandbox Code Playgroud)

然后像"b3ad5"这样的text/varchar值也会给出数字

db=> select CAST(REGEXP_REPLACE(COALESCE('b3ad5','0'), '[^0-9]+', '', 'g') AS INTEGER);
 regexp_replace
----------------
             35
(1 row)
Run Code Online (Sandbox Code Playgroud)

为了解决Chris Cogdon对解决方案的关注,不解决所有情况,包括诸如"坏"(完全没有数字字符)之类的情况,我做了这个调整后的声明:

CAST((COALESCE(NULLIF(REGEXP_REPLACE(myfield, '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
Run Code Online (Sandbox Code Playgroud)

它的工作方式类似于更简单的解决方案,除非转换的值仅为非数字字符时给出0,例如"bad":

db=> select CAST((COALESCE(NULLIF(REGEXP_REPLACE('no longer bad!', '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
     coalesce
----------
        0
(1 row)
Run Code Online (Sandbox Code Playgroud)

  • 只有当输入是整数或 NULL 时,解决方案才有效。问题是要求转换任何类型的输入,如果不可转换则使用 0。 (2认同)

Mat*_*att 20

这可能有点像黑客,但它在我们的案例中完成了工作:

(0 || myfield)::integer
Run Code Online (Sandbox Code Playgroud)

解释(在Postgres 8.4上测试):

上面提到的表达式产生空字符串和空字符串中的NULLNULL值(这种确切的行为可能适合您的用例,也可能不适合您的用例).myfield0

SELECT id, (0 || values)::integer from test_table ORDER BY id
Run Code Online (Sandbox Code Playgroud)

测试数据:

CREATE TABLE test_table
(
  id integer NOT NULL,
  description character varying,
  "values" character varying,
  CONSTRAINT id PRIMARY KEY (id)
)

-- Insert Test Data
INSERT INTO test_table VALUES (1, 'null', NULL);
INSERT INTO test_table VALUES (2, 'empty string', '');
INSERT INTO test_table VALUES (3, 'one', '1');
Run Code Online (Sandbox Code Playgroud)

该查询将产生以下结果:

 ---------------------
 |1|null        |NULL|
 |2|empty string|0   |
 |3|one         |1   |
 ---------------------
Run Code Online (Sandbox Code Playgroud)

而选择仅values::integer会导致错误消息.

希望这可以帮助.


Erw*_*ter 5

@Matthew 的回答很好。但它可以更简单、更快。问题要求将空字符串 ( '')转换为0,而不是其他“无效输入语法”或“超出范围”输入:

CREATE OR REPLACE FUNCTION convert_to_int(text)
  RETURNS int AS
$func$
BEGIN
   IF $1 = '' THEN  -- special case for empty string like requested
      RETURN 0;
   ELSE
      RETURN $1::int;
   END IF;

EXCEPTION WHEN OTHERS THEN
   RETURN NULL;  -- NULL for other invalid input

END
$func$  LANGUAGE plpgsql IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)

这将返回0空字符串和NULL任何其他无效输入。
它可以很容易地适应任何数据类型转换

进入异常块的成本要高得多。如果空字符串很常见,那么在引发异常之前捕获这种情况是有意义的。
如果空字符串非常罕见,则将测试移至异常子句是值得的。