2013-04-24 90 views
25

我需要確定給定的字符串是否可以在SQL語句中被解釋爲數字(整數或浮點數)。如下所示:帶PostgreSQL的isnumeric()

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

我發現Postgres的pattern matching可以用於此目的。因此,我修改了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) 

正如你可以看到,前兩個項目(空字符串''和唯一的時期'.')會被誤判爲數值型(他們不是)。目前我無法接近這一點。任何幫助感謝!


更新基於this answer(及其評論),我適應的模式:

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) 

仍然有一些問題與科學正如我現在所看到的,符號和負數。

+1

您是否需要擔心負數?科學記數法如何? – 2013-04-24 16:28:46

+0

@ muistooshort再次感謝,我對這種輸入特別感興趣。這種模式匹配方法並不像我預期的那樣直截了當。 – moooeeeep 2013-04-24 19:34:55

+1

負數的正則表達式很簡單: ''^ - ?([0-9] + [。]?[0-9] * | [。] [0-9] +)$''正確嗎? – 2015-10-15 16:27:45

回答

58

正如您可能已經注意到的那樣,基於正則表達式的方法幾乎不可能正確執行。例如,你的測試說1.234e-5是無效的數字,當它是真的。此外,你錯過了負數。如果東西看起來像一個數字,但是當你試圖存儲它會導致溢出?

相反,我會建議創建一個嘗試(如果你的工作需要,或FLOAT),並根據這個轉換是否成功與否TRUEFALSE返回到實際轉換爲NUMERIC功能。

此代碼將完全模擬功能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) 

它不僅是更正確,更易於閱讀,它也將如果數據實際上是一個數字,則工作更快

+0

1.234d + 5也是一個「有效」數字。我在幾年前做了一些數據倉庫工作的格式。它在一箇舊的Fortran程序的輸出中;它表示一個雙精度浮點值。無論他們正確導入它的任何辦公軟件。 – 2013-04-25 04:07:37

+3

嗯,我的觀點是,如果你想說如果Postgres數據庫中存儲的字符串是一個有效的數字,唯一合理的方法是向Postgres服務器本身詢問它對它的看法。如果它說'1.234d + 5'不是一個有效的數字,那麼你不能使用Postgres方法將它真正地轉換爲有效的數字。 – mvp 2013-04-25 04:14:21

+0

我會稍微修改它來處理NULL:'FUNCTION isnumeric(anyelement)'來接受任何參數。然後爲成功的操作返回動態值:'DECLARE x NUMERIC;結果BOOLEAN;'在BEGIN塊內設置該值:'results = CASE WHEN $ 1 IS NULL THEN NULL ELSE TRUE END; x = $ 1 :: NUMERIC;返回結果;'---這意味着isnumeric(NULL)將返回NULL,因爲NULL沒有值。 – vol7ron 2014-02-24 21:01:25

10

你的問題是小數點每邊有兩個0或更多的[0-9]元素。你需要使用一個邏輯或|在號碼識別線:

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

這將單獨排除小數點作爲一個有效的數字。

+4

您錯過了'.'s上的一些轉義符,它會匹配'1x1'和'x1'。 – 2013-04-24 16:27:44

+0

是的,我習慣了甲骨文和Java,請確保你的逃生是正確的,應該是正確的POSIX OR操作符和。應該是PERIOD,而不是POSIX的「全角色」操作員。 – 2013-04-24 17:05:59