2011-12-21 16 views
28

Zapytanie Pg zwraca tablicę. Chciałbym to pobrać z każdym elementem sformatowanym do 3 miejsc dziesiętnych. Jak mogę zastosować funkcję do każdego elementu tablicy? Coś jak poniżej (tak, oczywiście) -Jak zastosować funkcję do każdego elementu kolumny tablicy w PostgreSQL?

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

Chyba szukam czegoś podobnego map funkcji Perl.

+0

niesamowite sugestie od wszystkich. Myślę, że pójdę z przechowywanym procem, ponieważ muszę przez cały czas stosować tę funkcję 'map'. Byłoby jeszcze lepiej, gdybym mógł przekazać funkcję do przechowywanego procesu, tworząc w ten sposób przechowywany fabrycznie proces, który przekształciłby go w prawdziwą funkcję "map". Ale to zadziała na razie. Jeszcze raz dziękuję, wszystkim. – punkish

+0

Re: przekazywanie funkcji: Może Cię zainteresować http://stackoverflow.com/questions/8346065/function-as-parametr- na -inne-funkcje-w-postgres. (To daleki od ideału, ale możesz z niego skorzystać.) – ruakh

Odpowiedz

8

Może być konieczne utworzenie zapisanej funkcji. Oto ten, który robi to, co trzeba:

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; 

Następnie nazwać coś takiego:

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

Musisz włączyć tablicę na zestaw wierszy. Na przykład za pomocą generate_series:

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

Wiem, że to dość brzydki. Powinna istnieć funkcja pomocnicza ułatwiająca takie mapowanie.

Być może coś takiego (tak to straszne, powolne i kruche kod dynamiczny):

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} 

Aktualizacja Wydaje mi się, że możemy korzystać z jednego dynamicznego oświadczenie dla całej pętli. Może to złagodzić niektóre obawy dotyczące wydajności.

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

Dynamiczna pętla wewnętrzna SQL to naprawdę okropny pomysł. Nie rób tego w plpgsql. Jest to względny język statyczny i nie można tutaj zastosować wzorców z języków perl, python lub innych języków skryptowych. –

+1

Tak, to właśnie moje zdanie przed kodem mówi. Uciekłem do tego w tym dość kapryśnym przykładzie, ponieważ chcę przekazać funkcję, która ma być wywołana w plpgsql. To podstawowa abstrakcja w programowaniu i przydatna rzecz do zrobienia w dowolnym języku. Oczywiście to jest ból, który musimy zrobić jako ciąg, a następnie dynamicznie konstruować nowe wyrażenie, ale jest to coś, co należy wziąć pod uwagę, jeśli kiedykolwiek chcesz napisać ogólną funkcję mapowania. Alternatywą jest napisanie 100 nie-ogólnych funkcji mapowania. – Edmund

+0

jeśli potrzebujesz tej formy abstrakcji, użyj własnych modułów C. PL/pgSQL nie jest językiem abstrakcyjnego kodowania.Każdy nieefektywny kod po stronie bazy danych może znacznie zmniejszyć wydajność. map_with_args powinno być względnie po prostu zaimplementowane w C –

68

Najpierw włącz tablicę do zestawu za pomocą unnest:

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

Następnie zastosować wyrażenia do kolumny:

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

Wreszcie, należy array_agg aby włączyć cofnięty w tablicy:

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

I można to również zawrzeć w zdefiniowanej przez użytkownika funkcji 'round (int [], int)'. –

+5

Jest to najbardziej zwięzłe rozwiązanie i podoba mi się jasny sposób, w jaki go konstruujesz. – Edmund

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

Najlepsza odpowiedź tutaj – sudo

+0

Istnieje pewna różnica w wydajności między 'array_agg()' (patrz rozwiązanie Joey'a) i 'array()' (to rozwiązanie)? –

+1

@PeterKrauss Zrobiłem test porównawczy teraz, a prędkość jest taka sama –

Powiązane problemy