文字列を整数にキャストし、PostgreSQLでのキャストでエラーが発生した場合は0にするにはどうすればよいですか?


128

PostgreSQLでは、varchar列を持つテーブルがあります。データは整数であることが想定されており、クエリで整数型で必要です。一部の値は空の文字列です。以下:

SELECT myfield::integer FROM mytable

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

postgresでのキャスト中にエラーが発生した場合、キャストを照会して0にするにはどうすればよいですか?

回答:


161

私自身も同様の問題に取り組んでいましたが、関数のオーバーヘッドは必要ありませんでした。私は次のクエリを思いつきました:

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

Postgresはその条件文をショートカットするので、:: integerキャストに非整数が当たらないようにしてください。また、NULL値も処理します(正規表現とは一致しません)。

選択せずにゼロが必要な場合は、CASEステートメントが機能します。

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

14
マシューの提案に従うことを強くお勧めします。このソリューションには、数値のように見えるが整数に配置できる最大値より大きい文字列に関する問題があります。
ピリフ

4
二番目のピリフのコメント。その最大値は、発生するのを待っているバグです。エラーをスローしないことのポイントは、データが無効な場合にエラーをスローしないことです。この受け入れられた答えはそれを解決しません。ありがとうマシュー!すごい仕事!
Shawn Kovac 2014

3
マシューの答えと同じくらい素晴らしいですが、私はいくつかのデータをチェックするための迅速で汚い処理方法が必要でした。また、SQLで関数を定義するとき、自分の知識が現時点で不足していることも認めます。1桁から5桁の数字だけに興味があったので、正規表現をに変更しましたE'\\d{1,5}$'
2016年

3
はい、はい。このソリューションは比較的迅速でダーティですが、私の場合、自分が持っているデータと、テーブルが比較的短いことがわかっていました。関数全体を作成(およびデバッグ)するよりもはるかに簡単です。@Bobortの{1,5}桁数に関する上記の制限は、オーバーフローが心配な場合は良い考えですが、大きい数値をマスクするため、テーブルを変換する場合に問題が発生する可能性があります。個人的には、クエリエラーを事前に用意しておき、私の「整数」の一部が厄介であることを知っています(E'\\d{6,}$'最初にを選択して確認することもできます)。
Anthony Briggs

1
@Anthony Briggs:myfieldに "'"または "、"または "。"または'-'が含まれている場合、これは機能しません
Stefan Steiger

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
受け入れられた回答とは対照的に、ここでのこのソリューションは、整数に収まらないほど大きな数値を同等に処理できるため、より正確です。また、一般的なケース(=有効な文字列)
ピリフ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)

次に、 "b3ad5"のようなtext / varchar値も数値になります

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

「悪い」(数字文字がま​​ったくない)などのケースを含め、すべてのケースで0を与えない解決策に関するChris Cogdonの懸念に対処するために、次のように調整したステートメントを作成しました。

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

これは、変換する値が「bad」などの数字以外の文字のみの場合に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 True。ナイスキャッチ。編集。
ghbarratt

1
ソリューションは、入力が整数またはNULLの場合にのみ機能します。質問は、あらゆる種類の入力を変換するように求めていました。変換できない場合は0を使用してください。
クリスコグドン

@ChrisCogdon変換する値が「変換できない」場合に常にゼロを与えるとは限らないという懸念に対処するために、ソリューションに追加しました。このソリューションの微調整されたバージョンは、変換する値として数字のない文字列が指定された場合、0を返します。
ghbarratt

22

これは多少ハックのように見えるかもしれませんが、私たちの場合、それは仕事を成し遂げました:

(0 || myfield)::integer

説明(Postgres 8.4でテスト済み):

上記の式はNULLmyfieldおよびのNULL値を生成します0空文字列のために(この正確な動作をしたり、ユースケースに合うかもしれません)。

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čJan

3

@マシューの答えは良いです。しかし、それはより簡単でより速くなることができます。そして、質問は空の文字列('')をに変換するように求めます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その他の無効な入力に対して返されます。あらゆるデータ型変換に
簡単に適合できます

例外ブロックに入るのはかなり高価です。空の文字列が一般的である場合、例外を発生させる前にそのケースをキャッチすることは理にかなっています。
空の文字列が非常にまれである場合、テストを例外句に移動することは価値があります。


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


正規表現と文字列関数のパフォーマンステストを実行しましたか?また、これはnullをどのように処理しますか?期待どおりに0またはNULLを返しますか?ありがとう!
vol7ron


1

SUBSTRINGが役立つ場合があります。intのサイズを制限できます。

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

0

データが整数であると想定されていて、それらの値が整数として必要なだけの場合、マイルを丸ごと移動して列を整数列に変換しませんか?

その後、データがテーブルに挿入されるシステムの時点で、不正な値のゼロへの変換を一度だけ実行できます。

上記の変換では、Postgresにそのテーブルの各クエリの単一行ごとにそれらの値を何度も変換するように強制しています。このテーブルのこの列に対して多くのクエリを実行すると、パフォーマンスが大幅に低下する可能性があります。


原則的にはあなたの言うとおりですが、この特定のシナリオでは、アプリケーションで単一の遅いクエリを最適化する必要があります。データ入力を処理するコードがどのように機能するのかわかりません。触りたくない。これまでのところ、書き換えられたクエリは機能しますが、予期しないケースで壊れないようにしたいと思います。アプリケーションを再構築することは、たとえそれが最も賢明なことのように思えても、オプションではありません。
silviot 2010年

0

次の関数は

  • error_resultキャストできない結果にはデフォルト値()を使用します。abcまたは999999999999999999999999999999999999999999
  • 保つ nullするnull
  • 入力のスペースやその他の空白を削除します
  • 有効としてキャストされた値bigintslower_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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.