web-dev-qa-db-ja.com

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

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

SELECT myfield::integer FROM mytable

ERROR: invalid input syntax for integer: ""を生成します

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

111
silviot

私は自分で同様の問題に取り組んでいたが、関数のオーバーヘッドを望んでいなかった。次のクエリを思い付きました。

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;
144
Anthony Briggs

独自の変換関数を作成することもできます。その内部でcan例外ブロックを使用します。

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)
87
Matthew Wood

私は同じ種類のニーズがあり、これが私にとってうまくいくことがわかりました(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)
25
ghbarratt

これはややハックかもしれませんが、私たちのケースでは仕事が完了しました:

(0 || myfield)::integer

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

上記の式は、空文字列のNULLおよび0のNULL値に対してmyfieldを生成します(この正確な動作は、ユースケースに適合する場合と適合しない場合があります)。

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のみを選択すると、エラーメッセージが表示されます。

お役に立てれば。

19
Matt

@ Matthew's answer は良い。しかし、それはよりシンプルで高速です。そして、質問は空の文字列('')を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を返します。
anyデータ型変換に簡単に適応できます。

例外ブロックを入力すると、かなり費用がかかります。空の文字列がcommonである場合、例外を発生させる前にそのケースをキャッチすることは理にかなっています。
空の文字列が非常にまれな場合、例外節にテストを移動することは有益です。

3

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

PostgreSQLを使用したことはありませんが、SELECTクエリのIFステートメントの正しい構文について manual を確認しました。

3
Jan Hančič
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を返します

1
Oleg Mikhailov

次のコードは簡単で機能していることがわかりました。元の回答はこちら https://www.postgresql.org/message-id/[email protected]

prova=> create table test(t text, i integer);
CREATE

prova=> insert into test values('123',123);
INSERT 64579 1

prova=> select cast(i as text),cast(t as int)from test;
text|int4
----+----
123| 123
(1 row)

それが役に立てば幸い

1
Ashish Rana

私も同じニーズを持っていますが、それはJPA 2.0とHibernate 5.0.2で動作します:

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

驚異的です。 LIKEでも機能すると思います。

0
Hendy Irawan

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

次に、データがテーブルに挿入されるシステムのポイントで、不正な値をゼロに一度だけ変換することができます。

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

0
Bandi-T