2012-10-29 11 views
43

Jeśli używam array_agg do zbierania nazw, otrzymuję moje nazwy oddzielone przecinkami, ale w przypadku, gdy istnieje wartość null, ta wartość zerowa jest również brana jako nazwa w agregacie. Na przykład:Jak wykluczyć wartości null w array_agg jak w string_agg przy użyciu postgres?

SELECT g.id, 
     array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users, 
     array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users 
FROM groups g 
GROUP BY g.id; 

zwraca ,Larry,Phil zamiast tylko Larry,Phil (w moim 9.1.2, to pokazuje NULL,Larry,Phil). jak w this skrzypce

Zamiast tego, jeśli mogę użyć string_agg(), to pokazuje mi tylko nazwy (bez pustych przecinkami lub null) jak here

Problem polega na tym, że mam Postgres 8.4 zainstalowany na serwerze, a string_agg() robi” t tam pracuj. Czy istnieje sposób uczynienia array_agg działa podobnie do string_agg()?

+0

Zobacz ten PostgreSQL Mailing List Wątek na dużo tego tematu: http://postgresql.1045698.n5.nabble.com/array-agg-NULL-Handling-td2798942.html –

+0

mi przykro, I don” Sądzę, że istnieje rozwiązanie w tym wątku. – Daud

+0

Istnieją dwa rozwiązania w tym wątku. Jedną z nich jest stworzenie funkcji, a druga (tylko sugerowana, nie pokazana) jest tą, na którą odpowiedziałem. –

Odpowiedz

16

SQL Fiddle

select 
    id, 
    (select array_agg(a) from unnest(canonical_users) a where a is not null) canonical_users, 
    (select array_agg(a) from unnest(non_canonical_users) a where a is not null) non_canonical_users 
from (
    SELECT g.id, 
      array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users, 
      array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users 
    FROM groups g 
    GROUP BY g.id 
) s 

Albo prostsze i może być tańsze, używając array_to_string co eliminuje null:

SELECT 
    g.id, 
    array_to_string(
     array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) 
     , ',' 
    ) canonical_users, 
    array_to_string(
     array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) 
     , ',' 
    ) non_canonical_users 
FROM groups g 
GROUP BY g.id 

SQL Fiddle

+1

To nie zadziała. Proszę, wypróbuj go w skrzypcach, które dostarczyłem. – Daud

+0

Dzięki. Ale jeśli główna kwerenda (s) zwraca 1000 wierszy, to 2 podkwerendy (przy użyciu unnest) uruchomi się raz dla każdego wiersza .. Czy będzie lepiej tolerować NULL niż wykonanie 2000 dodatkowych kwerend select? – Daud

+0

@Daud Nowa wersja, która może być tańsza. Przyjrzyj się wyjściu wyjaśniającemu obu, aby się upewnić. –

3

Jak sugerowano w komentarzach można napisać funkcja zastępująca wartości null w tablicy, jednak jak wskazano w wątku połączonym w komentarzach ten rodzaj pokonuje efektywność funkcji zagregowanej, jeśli trzeba utworzyć agregat, podzielić go, a następnie ponownie zagregować.

Myślę, że utrzymanie wartości null w tablicy jest po prostu (być może niechcianym) elementem Array_Agg. Można użyć podzapytania tego uniknąć:

SELECT COALESCE(y.ID, n.ID) ID, 
     y.Users, 
     n.Users 
FROM ( SELECT g.ID, ARRAY_AGG(g.Users) AS Users 
      FROM Groups g 
      WHERE g.Canonical = 'Y' 
      GROUP BY g.ID 
     ) y 
     FULL JOIN 
     ( SELECT g.ID, ARRAY_AGG(g.Users) AS Users 
      FROM Groups g 
      WHERE g.Canonical = 'N' 
      GROUP BY g.ID 
     ) n 
      ON n.ID = y.ID 

SQL FIDDLE

+0

Dzięki. Ale potrzebowałem "przypadku" do obsłużenia wierszy w danej grupie, a podzapytania byłyby nieskuteczne. – Daud

-3

Większy pytanie dlaczego wyciągnąć wszystkie kombinacje użytkownik/grupa naraz. Gwarantowany twój UI nie może obsłużyć wszystkich tych danych. Dodawanie stronicowania do zbyt dużych danych również jest złym pomysłem. Poproś użytkowników, aby przefiltrowali zestaw, zanim zobaczą dane. Upewnij się, że zestaw opcji JOIN znajduje się na liście, aby mogły filtrować skuteczność, jeśli chcą. Czasami 2 zapytania sprawiają, że użytkownicy są bardziej zadowoleni, jeśli oboje są szybcy.

10

Rozwiązując ogólne pytanie dotyczące usuwania wartości null z agregatów tablicowych, istnieją dwa główne sposoby na zaatakowanie problemu: albo robi array_agg (unnest (array_agg (x)) lub tworzy niestandardowy agregat.

Pierwszy ma postać pokazaną above:

SELECT 
    array_agg(u) 
FROM (
    SELECT 
     unnest(
      array_agg(v) 
     ) as u 
    FROM 
     x 
    ) un 
WHERE 
    u IS NOT NULL; 

Drugi:

/* 
With reference to 
http://ejrh.wordpress.com/2011/09/27/denormalisation-aggregate-function-for-postgresql/ 
*/ 
CREATE OR REPLACE FUNCTION fn_array_agg_notnull (
    a anyarray 
    , b anyelement 
) RETURNS ANYARRAY 
AS $$ 
BEGIN 

    IF b IS NOT NULL THEN 
     a := array_append(a, b); 
    END IF; 

    RETURN a; 

END; 
$$ IMMUTABLE LANGUAGE 'plpgsql'; 

CREATE AGGREGATE array_agg_notnull(ANYELEMENT) (
    SFUNC = fn_array_agg_notnull, 
    STYPE = ANYARRAY, 
    INITCOND = '{}' 
); 

Wywołanie drugi jest (oczywiście) trochę ładniejszy patrząc niż pierwszy:

wybierz array_agg_notnull (v) od x;

5

Dodaję to, mimo że ten wątek jest dość stary, ale wpadłem na tę fajną sztuczkę, która działa całkiem dobrze na małych tablicach. Działa na Postgres 8.4+ bez dodatkowych bibliotek i funkcji.

string_to_array(array_to_string(array_agg(my_column)))::int[] 

Metoda faktycznie pozbawia wartości zerowych.

122

Z postgresql-9.3 można to zrobić;

SELECT g.id, 
    array_remove(array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END), NULL) canonical_users, 
    array_remove(array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END), NULL) non_canonical_users 
FROM groups g 
GROUP BY g.id; 

Aktualizacja: z postgresql-9.4;

SELECT g.id, 
    array_agg(g.users) FILTER (WHERE g.canonical = 'Y') canonical_users, 
    array_agg(g.users) FILTER (WHERE g.canonical = 'N') non_canonical_users 
FROM groups g 
GROUP BY g.id; 
+3

To działa i jest szybkie i eleganckie, rozwiązało mnie problem podobny do PO. Powód do aktualizacji do 9.3 dla tych, którzy jeszcze tego nie zrobili. +1 –

+6

9.4 jest jeszcze bardziej elegancki. Działa jak urok. – jmgarnier

+0

Wersja 9.4 jest jeszcze lepsza, ponieważ to, czego potrzebuję odfiltrować w moim przypadku, to wartości zerowe. – coladict

Powiązane problemy