我如何将字符串转换为整数,并且在PostgreSQL转换时出现错误的情况下为0?


128

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

SELECT myfield::integer FROM mytable

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

如何在postgres中进行强制转换时查询强制转换并在错误的情况下设置为0?

Answers:


161

我本人只是在努力解决类似的问题,但不想增加函数的开销。我想出了以下查询:

SELECT myfield::integer FROM mytable WHERE myfield ~ E'^\\d+$';

Postgres缩短了它的条件条件,因此您不应让:: integer演员遇到任何非整数。它还处理NULL值(它们与regexp不匹配)。

如果您想要零而不是不选择,那么CASE语句应该起作用:

SELECT CASE WHEN myfield~E'^\\d+$' THEN myfield::integer ELSE 0 END FROM mytable;

14
我强烈建议接受马修的建议。该解决方案存在字符串看起来像数字但大于可放置在整数中的最大值的字符串问题。
pilif 2012年

4
我第二次pilif的评论。最大值是一个等待发生的错误。不引发错误的目的是在数据无效时不引发错误。这个公认的答案不能解决这个问题。谢谢马修!做得好!
肖恩·科瓦奇

3
就像Matthew的回答一样,我只需要一种快速而肮脏的处理方式来检查某些数据。我也承认,现在我在SQL中定义函数时缺乏我自己的知识。我只对1到5位数之间的数字感兴趣,因此我将正则表达式更改为E'\\d{1,5}$'
Bobort's

3
是的,是的,此解决方案相对快速且肮脏,但就我而言,我知道自己拥有的数据以及表相对较短。这比编写(和调试)整个函数要容易得多。{1,5}如果您担心溢出,则@Bobort 在数字上的限制可能是个好主意,但它将掩盖较大的数字,如果要转换表,可能会造成麻烦。就我个人而言,我宁愿预先提出查询错误,并且知道我的某些“整数”是棘手的(您也可以先选择with E'\\d{6,}$'来确保)。
安东尼·布里格斯

1
@Anthony布里格斯:这是不行的,如果MyField的含有“'‘或’,”或者,“。”或‘ - ’
斯特凡·斯泰格尔

100

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

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;

测试:

=# 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)

8
与公认的答案相反,此解决方案在这里更正确,因为它可以很好地处理太大而无法容纳整数的数字,并且由于在通常情况下不进行验证(=有效字符串)而可能更快)
pilif 2012年

你会如何施展串入上使用功能的特定字段的整数,而INSERT声明中?
sk

27

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

CAST((COALESCE(myfield,'0')) AS INTEGER)

一些测试用例演示:

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"

如果需要处理该字段包含非数字文本(例如“ 100bad”)的可能性,则可以使用regexp_replace在转换前去除非数字字符。

CAST(REGEXP_REPLACE(COALESCE(myfield,'0'), '[^0-9]+', '', 'g') AS INTEGER)

然后,文本/ varchar值(例如“ b3ad5”)也会给出数字

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

为了解决克里斯·科格登(Chris Cogdon)的关注,即在所有情况下(包括“坏”(根本没有数字字符))都不给0的解决方案,我做了以下调整后的语句:

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

它的工作方式与较简单的解决方案相似,不同之处在于,当要转换的值仅是非数字字符(例如“坏”)时将给出0:

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

为什么需要'0'|| ?从文档中:“ COALESCE函数返回其第一个不为null的参数。” 因此,如果您将null用作值,那么Coalesce将摆脱它。
Amala 2015年

@Amala是的。不错的收获。编辑。
ghbarratt

1
仅当输入为整数或NULL时,解决方案才有效。问题是要求转换任何类型的输入,如果不能转换则使用0。
克里斯·科格登

@ChrisCogdon我已添加到解决方案中,以解决您的担忧,如果要转换的值是“不可转换的”,则不总是给出零。当将没有数字字符的字符串作为要转换的值时,此解决方案的调整版本将返回0。
ghbarratt

22

这可能有点骇人听闻,但在我们的案例中它完成了工作:

(0 || myfield)::integer

说明(在Postgres 8.4上测试):

上述表达产量NULL为NULL值myfield0为空字符串(这确切行为可能或可能不适合你的使用情况)。

SELECT id, (0 || values)::integer from test_table ORDER BY id

测试数据:

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');

该查询将产生以下结果:

 ---------------------
 |1|null        |NULL|
 |2|empty string|0   |
 |3|one         |1   |
 ---------------------

而仅选择 values::integer将导致错误消息。

希望这可以帮助。


3

SELECT CASE WHEN myfield="" THEN 0 ELSE myfield::integer END FROM mytable

我以前从未使用过PostgreSQL,但是我检查了手册中SELECT查询中IF语句的正确语法。


这适用于现在的表格。我有点担心,将来它可能包含非数字值。我本来希望使用类似try / catch的解决方案,但这确实有用。谢谢。
silviot 2010年

也许您可以使用正则表达式postgresql.org/docs/8.4/interactive/functions-matching.html,但这可能会花费很多。如果解决方案也可以接受答案:)
JanHančič10年

3

@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;

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

输入异常块实际上要贵得多。如果常见的是空字符串,则在引发异常之前捕获该情况是有意义的。
如果空字符串非常罕见,则需要将测试移至exception子句。


1
CREATE OR REPLACE FUNCTION parse_int(s TEXT) RETURNS INT AS $$
BEGIN
  RETURN regexp_replace(('0' || s), '[^\d]', '', 'g')::INT;
END;
$$ LANGUAGE plpgsql;

0如果输入字符串中没有数字,则此函数将始终返回。

SELECT parse_int('test12_3test');

将返回 123


您是否对正则表达式和字符串函数进行了性能测试?另外,这如何处理空值?是否会按预期返回0或NULL?谢谢!
vol7ron


1

在某些情况下,SUBSTRING可能会有所帮助,您可以限制int的大小。

SELECT CAST(SUBSTRING('X12312333333333', '([\d]{1,9})') AS integer);

0

如果数据应该是整数,而您只需要将这些值作为整数,那为什么不花一整心的时间将该列转换为整数列呢?

然后,您可以在将数据插入到表中的系统位置一次将非法值转换为零。

通过上述转换,您将迫使Postgres对该表的每个查询中的每一行一次又一次地转换这些值-如果您对此表中的该列进行大量查询,则可能严重降低性能。


原则上您是对的,但是在这种特定情况下,我必须优化应用程序中的单个慢查询。我不知道处理数据输入的代码如何工作。我不想碰它。到目前为止,我重写的查询仍然有效,但是我希望在不可预见的情况下不要中断它。即使看起来是最明智的选择,也无法重新设计应用程序。
silviot 2010年

0

以下功能

  • 使用默认值(error_result不可浇注结果EG)abc999999999999999999999999999999999999999999
  • 保持nullnull
  • 修剪输入中的空格和其他空白
  • bigints比较lower_bound强制转换为有效的值,例如仅强制执行正值
CREATE OR REPLACE FUNCTION cast_to_bigint(text) 
RETURNS BIGINT AS $$
DECLARE big_int_value BIGINT DEFAULT NULL;
DECLARE error_result  BIGINT DEFAULT -1;
DECLARE lower_bound   BIGINT DEFAULT 0;
BEGIN
    BEGIN
        big_int_value := CASE WHEN $1 IS NOT NULL THEN GREATEST(TRIM($1)::BIGINT, lower_bound) END;
    EXCEPTION WHEN OTHERS THEN
        big_int_value := error_result;
    END;
RETURN big_int_value;
END;

-1

我也有相同的需求,但可以与JPA 2.0和Hibernate 5.0.2一起使用:

SELECT p FROM MatchProfile p WHERE CONCAT(p.id, '') = :keyword

创造奇迹。我认为它也适用于LIKE。


-3

这也应该做的工作,但这是跨SQL而不是特定于Postgres的。

select avg(cast(mynumber as numeric)) from my table
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.