2013-04-24 24 views
25

Tôi cần phải xác định liệu một chuỗi đã cho có thể được hiểu là một số (số nguyên hoặc dấu chấm động) trong một câu lệnh SQL hay không. Như sau:isnumeric() với PostgreSQL

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

Tôi thấy rằng Postgres 'pattern matching có thể được sử dụng cho việc này. Và vì vậy tôi đã điều chỉnh tuyên bố được đưa ra trong this place để kết hợp các số dấu phẩy động. Đây là mã của tôi:

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; 

Sản lượng:

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) 

Như bạn có thể thấy, hai mục đầu tiên (chuỗi rỗng '' và giai đoạn duy nhất '.') đều được phân loại sai như là một loại số (mà họ không). Tôi không thể tiến gần hơn nữa vào lúc này. Bất kỳ trợ giúp nào được đánh giá cao!


Cập nhật Dựa trên this answer (và ý kiến ​​của mình), tôi thích nghi mô hình để:

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; 

Mà cho:

 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) 

Hiện vẫn còn một số vấn đề với các khoa học ký hiệu và với số âm, như tôi thấy bây giờ.

+1

Bạn có phải lo lắng về số âm? Làm thế nào về ký hiệu khoa học? –

+0

@muistooshort cảm ơn một lần nữa, tôi đặc biệt quan tâm đến kiểu đầu vào này. Cách tiếp cận đối sánh mẫu này không thẳng thắn như tôi mong đợi. – moooeeeep

+1

Regex cho số âm đơn giản là: ''^ -? ([0-9] + [.]? [0-9] * | [.] [0-9] +) $'' đúng? –

Trả lời

58

Như bạn có thể nhận thấy, phương pháp dựa trên regex hầu như không thể thực hiện chính xác. Ví dụ: thử nghiệm của bạn cho biết rằng 1.234e-5 không phải là số hợp lệ, khi thực sự là như vậy. Ngoài ra, bạn đã bỏ lỡ số âm. Điều gì nếu một cái gì đó trông giống như một số, nhưng khi bạn cố gắng để lưu trữ nó sẽ gây ra tràn?

Thay vào đó, tôi muốn giới thiệu để tạo chức năng mà cố gắng để thực sự đúc để NUMERIC (hoặc FLOAT nếu nhiệm vụ của bạn yêu cầu nó) và trả về TRUE hoặc FALSE tuỳ thuộc vào việc dàn diễn viên này đã thành công hay không.

Mã này sẽ mô phỏng đầy đủ chức năng 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; 

gọi hàm này trên dữ liệu của bạn được kết quả như sau:

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) 

Không chỉ nó là chính xác hơn và dễ dàng hơn để đọc, nó sẽ còn làm việc nhanh hơn nếu dữ liệu thực sự là một số.

+0

1.234d + 5 cũng là số "hợp lệ". Tôi chạy vào định dạng đó làm một số kho dữ liệu làm việc một vài năm trước đây. Đó là trong đầu ra của một chương trình Fortran cũ; nó đại diện cho giá trị float chính xác gấp đôi. Dù phần mềm văn phòng nào họ đã nhập chính xác. –

+3

Vâng, quan điểm của tôi là nếu bạn đang cố gắng nói nếu chuỗi được lưu trữ trong cơ sở dữ liệu Postgres là một số hợp lệ, phương pháp hợp lý duy nhất là hỏi chính máy chủ Postgres về những gì nó nghĩ về nó. Nếu nó nói rằng '1,234d + 5' không phải là một số hợp lệ, thì bạn không thể thực sự đúc nó thành số hợp lệ bằng cách sử dụng Postgres. – mvp

+0

Tôi sửa đổi nó một chút để xử lý NULL: 'FUNCTION isnumeric (anyelement)' để lấy bất kỳ đối số nào. Sau đó trả về giá trị động cho các hoạt động thành công: 'DECLARE x NUMERIC; kết quả BOOLEAN; 'Và đặt giá trị đó bên trong khối BEGIN:' results = CASE WHEN $ 1 IS NULL THEN NULL ELSE TRUE END; x = $ 1 :: NUMERIC; RETURN results; '--- Điều này có nghĩa là isnumeric (NULL) sẽ trả về NULL, vì NULL không có giá trị. – vol7ron

10

Bạn có vấn đề là hai phần tử 0 hoặc hơn [0-9] ở mỗi bên của dấu thập phân. Bạn cần phải sử dụng một OR hợp lý | trong dòng nhận dạng số:

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

Điều này sẽ chỉ loại trừ một dấu thập phân làm số hợp lệ.

+4

Bạn đang bỏ lỡ một số lần thoát trên '.'s', điều đó sẽ khớp với cả '' 1x1'' và ''x1''. –

+0

Có, tôi đã quen với Oracle và Java, hãy đảm bảo thoát của bạn là chính xác, | phải là toán tử POSIX OR thích hợp và. phải là PERIOD, không phải nhà điều hành của tất cả nhân vật POSIX. –