2011-12-21 54 views
28

Truy vấn Pg trả về một mảng. Tôi muốn truy xuất điều đó với mỗi phần tử được định dạng thành 3 chữ số thập phân. Làm thế nào tôi có thể áp dụng một hàm cho mỗi phần tử của một mảng? Một cái gì đó như sau (sai, rõ ràng) -Làm thế nào để áp dụng một hàm cho mỗi phần tử của một cột mảng trong Postgres?

SELECT Round(ARRAY[1.53224,0.23411234], 2); 
{1.532, 0.234} 

Tôi đoán tôi đang tìm kiếm một cái gì đó như Perl's map chức năng.

+0

các đề xuất tuyệt vời từ mọi người. Tôi nghĩ rằng tôi sẽ đi với proc được lưu trữ vì tôi cần phải áp dụng loại chức năng 'map' này mọi lúc. Nó sẽ còn lớn hơn nếu tôi có thể chuyển một hàm vào proc được lưu trữ, do đó biến nó thành một nhà máy được lưu trữ proc để chuyển nó thành một hàm 'map' thực sự. Nhưng, điều này sẽ làm việc cho bây giờ. Cảm ơn một lần nữa, tất cả mọi người. – punkish

+0

Re: truyền trong một hàm: Bạn có thể quan tâm đến http://stackoverflow.com/questions/8346065/function-as-parameter-to-another-function-in-postgres. (It's xa lý tưởng, nhưng bạn có thể nhận được một số sử dụng từ nó.) – ruakh

Trả lời

8

Bạn có thể cần phải tạo một hàm được lưu trữ. Đây là một trong đó làm những gì bạn cần:

CREATE OR REPLACE FUNCTION array_round(float[], int) 
RETURNS float[] 
AS 
$$ 
DECLARE 
    arrFloats ALIAS FOR $1; 
    roundParam ALIAS FOR $2; 
    retVal float[]; 
BEGIN 
    FOR I IN array_lower(arrFloats, 1)..array_upper(arrFloats, 1) LOOP 
    retVal[I] := round(CAST(arrFloats[I] as numeric), roundParam); 
    END LOOP; 
RETURN retVal; 
END; 
$$ 
LANGUAGE plpgsql 
    STABLE 
RETURNS NULL ON NULL INPUT; 

Sau đó gọi một cái gì đó như thế này:

# SELECT array_round(ARRAY[1.53224,0.23411234], 2); 
array_round 
------------- 
{1.53,0.23} 
(1 row) 
0

Bạn cần phải bật mảng thành một tập hàng. Ví dụ: sử dụng generate_series:

SELECT ARRAY(SELECT ROUND(ARRAY[1.53224,0.23411234])[i], 2) FROM generate_series(1,2) AS s(i));  

Tôi biết điều đó thật xấu xí. Nên có một hàm trợ giúp để tạo các ánh xạ như vậy dễ dàng hơn.

Có lẽ một cái gì đó tương tự (có nó là khủng khiếp, chậm, và giòn mã ðộng):

CREATE OR REPLACE FUNCTION map_with_arg(TEXT, ANYARRAY, TEXT) 
RETURNS ANYARRAY 
IMMUTABLE STRICT 
LANGUAGE 'plpgsql' AS 
$$ 
DECLARE 
    i INTEGER; 
    t TEXT; 
    cmd TEXT; 
BEGIN 
    FOR i IN array_lower($2, 1) .. array_upper($2, 1) LOOP 
     cmd := 'SELECT ('||quote_ident($1)||'('||quote_nullable($2[i])||', '||quote_nullable($3)||'))::TEXT'; 
     EXECUTE cmd INTO t; 
     $2[i] := t; 
    END LOOP; 
    RETURN $2; 
END; 
$$; 

select map_with_arg('repeat', array['can','to']::TEXT[], '2'); 
map_with_arg 
--------------- 
{cancan,toto} 

Cập nhật Nó xảy ra với tôi rằng chúng ta có thể sử dụng một tuyên bố động duy nhất cho toàn bộ vòng lặp. Điều này có thể giảm thiểu một số mối quan tâm về hiệu suất.

CREATE OR REPLACE FUNCTION map_with_arg(TEXT, ANYARRAY, TEXT) 
RETURNS ANYARRAY 
IMMUTABLE STRICT 
LANGUAGE 'plpgsql' AS 
$$ 
DECLARE 
    cmd TEXT; 
    rv TEXT; 
BEGIN 
    cmd := 'SELECT ARRAY(SELECT (' || quote_ident($1)||'($1[i], '||quote_nullable($3)||'))::TEXT FROM generate_subscripts($1, 1) AS gs(i))'; 
    EXECUTE cmd USING $2 INTO rv; 
    RETURN rv; 
END; 
$$; 
+1

Dynamic SQL bên trong vòng lặp thực sự là ý tưởng khủng khiếp. Đừng làm điều đó trong plpgsql. Đây là ngôn ngữ tĩnh tương đối và các mẫu từ ngôn ngữ perl, python hoặc các ngôn ngữ kịch bản khác không thể được áp dụng ở đây. –

+1

Vâng, đó là câu của tôi trước khi mã nói. Tôi sử dụng nó trong ví dụ đáng ngờ này vì tôi muốn chuyển hàm này thành plpgsql. Đó là một trừu tượng cơ bản trong lập trình và một điều hữu ích để có thể làm bằng bất kỳ ngôn ngữ nào. Tất nhiên đó là một nỗi đau mà chúng ta cần phải làm như một chuỗi và sau đó xây dựng một tuyên bố mới động, nhưng đó là một cái gì đó để được xem xét nếu bạn đã bao giờ muốn viết một chức năng lập bản đồ chung. Cách khác là phải viết 100 hàm ánh xạ không chung. – Edmund

+0

nếu bạn cần hình thức trừu tượng này, sau đó sử dụng một mô-đun C riêng. PL/pgSQL không phải là ngôn ngữ để viết mã trừu tượng.Bất kỳ mã không hiệu quả nào về phía cơ sở dữ liệu đều có thể làm giảm hiệu suất đáng kể. map_with_args nên tương đối đơn giản được thực hiện trong C –

68

Đầu tiên, lần lượt các mảng thành một tập sử dụng unnest:

> SELECT n FROM unnest(ARRAY[1.53224,0.23411234]) AS n; 
    n  
------------ 
    1.53224 
0.23411234 
(2 rows) 

Sau đó, áp dụng một biểu thức để cột:

> SELECT ROUND(n, 2) FROM unnest(ARRAY[1.53224,0.23411234]) AS n; 
round 
------- 
    1.53 
    0.23 
(2 rows) 

Cuối cùng, sử dụng array_agg để biến thiết lập trở lại thành một mảng:

> SELECT array_agg(ROUND(n, 2)) FROM unnest(ARRAY[1.53224,0.23411234]) AS n; 
    array_agg 
------------- 
{1.53,0.23} 
(1 row) 
+2

Và bạn có thể kết hợp nó thành một hàm 'round (int [], int)' do người dùng định nghĩa. –

+5

Đây là giải pháp ngắn gọn nhất và tôi thích cách rõ ràng bạn xây dựng nó. – Edmund

20
postgres=# select array(select round(unnest(array[1.2,2.4,3,4]))); 
    array 
----------- 
{1,2,3,4} 
(1 row) 
+0

Câu trả lời hay nhất ở đây – sudo

+0

Có một số khác biệt hiệu suất giữa 'array_agg()' (xem giải pháp của Joey) và 'mảng()' (giải pháp này)? –

+1

@PeterKrauss Tôi đã chuẩn bị ngay bây giờ và tốc độ là như nhau –

Các vấn đề liên quan