web-dev-qa-db-ja.com

PostgreSQLでのisnumeric()

特定の文字列をSQLステートメント内の数値(整数または浮動小数点)として解釈できるかどうかを判断する必要があります。次のように:

SELECT AVG(CASE WHEN x ~ '^[0-9]*.?[0-9]*$' THEN x::float ELSE NULL END) FROM test

Postgresの パターンマッチング がこれに使用できることがわかりました。そして、 this place で与えられたステートメントを適応させて、浮動小数点数を組み込みました。これは私のコードです:

WITH test(x) AS (
    VALUES (''), ('.'), ('.0'), ('0.'), ('0'), ('1'), ('123'),
    ('123.456'), ('abc'), ('1..2'), ('1.2.3.4'))

SELECT x
     , x ~ '^[0-9]*.?[0-9]*$' AS isnumeric
FROM test;

出力:

    x    | isnumeric 
---------+-----------
         | t
 .       | t
 .0      | t
 0.      | t
 0       | t
 1       | t
 123     | t
 123.456 | t
 abc     | f
 1..2    | f
 1.2.3.4 | f
(11 rows)

ご覧のとおり、最初の2つの項目(空の文字列''および唯一の期間'.')数値型であると誤分類されます(そうではありません)。現時点ではこれに近づくことはできません。助けていただければ幸いです!


Updateこの回答 (およびそのコメント)に基づいて、パターンを次のように適合させました。

WITH test(x) AS (
    VALUES (''), ('.'), ('.0'), ('0.'), ('0'), ('1'), ('123'),
    ('123.456'), ('abc'), ('1..2'), ('1.2.3.4'), ('1x234'), ('1.234e-5'))

SELECT x
     , x ~ '^([0-9]+[.]?[0-9]*|[.][0-9]+)$' AS isnumeric
FROM test;

与えるもの:

     x    | isnumeric 
----------+-----------
          | f
 .        | f
 .0       | t
 0.       | t
 0        | t
 1        | t
 123      | t
 123.456  | t
 abc      | f
 1..2     | f
 1.2.3.4  | f
 1x234    | f
 1.234e-5 | f
(13 rows)

私が今見ているように、科学表記法と負の数にはまだいくつかの問題があります。

32
moooeeeep

お気づきかもしれませんが、正規表現ベースの方法を正しく行うことはほとんど不可能です。たとえば、テストでは、_1.234e-5_は実際には有効な数値ではありません。また、負の数を逃しました。何かが数字のように見えても、それを保存しようとするとオーバーフローが発生しますか?

代わりに、実際にNUMERIC(またはタスクで必要な場合はFLOAT)にキャストし、TRUEまたはFALSEを返す関数を作成することをお勧めしますこのキャストが成功したかどうか。

このコードは、関数ISNUMERIC()を完全にシミュレートします。

_CREATE OR REPLACE FUNCTION isnumeric(text) RETURNS BOOLEAN AS $$
DECLARE x NUMERIC;
BEGIN
    x = $1::NUMERIC;
    RETURN TRUE;
EXCEPTION WHEN others THEN
    RETURN FALSE;
END;
$$
STRICT
LANGUAGE plpgsql IMMUTABLE;
_

データでこの関数を呼び出すと、次の結果が得られます。

_WITH test(x) AS ( VALUES (''), ('.'), ('.0'), ('0.'), ('0'), ('1'), ('123'),
  ('123.456'), ('abc'), ('1..2'), ('1.2.3.4'), ('1x234'), ('1.234e-5'))
SELECT x, isnumeric(x) FROM test;

    x     | isnumeric
----------+-----------
          | f
 .        | f
 .0       | t
 0.       | t
 0        | t
 1        | t
 123      | t
 123.456  | t
 abc      | f
 1..2     | f
 1.2.3.4  | f
 1x234    | f
 1.234e-5 | t
 (13 rows)
_

より正確で読みやすいだけでなく、データが実際に数値である場合にも高速に動作します。

70
mvp

問題は、小数点の両側にある2つ以上の[0-9]要素です。論理OR |番号識別行:

~'^([0-9]+\.?[0-9]*|\.[0-9]+)$'

これにより、有効な数値として小数点のみが除外されます。

11
Mr Rho