PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Schnittmenge mehrerer Arrays in PostgreSQL

Das, was einem Array-Schnittpunkt am nächsten kommt, ist Folgendes:

select array_agg(e)
from (
    select unnest(a1)
    intersect
    select unnest(a2)
) as dt(e)

Dies setzt voraus, dass a1 und a2 sind eindimensionale Arrays mit demselben Elementtyp. Sie könnten das in einer Funktion wie dieser zusammenfassen:

create function array_intersect(a1 int[], a2 int[]) returns int[] as $$
declare
    ret int[];
begin
    -- The reason for the kludgy NULL handling comes later.
    if a1 is null then
        return a2;
    elseif a2 is null then
        return a1;
    end if;
    select array_agg(e) into ret
    from (
        select unnest(a1)
        intersect
        select unnest(a2)
    ) as dt(e);
    return ret;
end;
$$ language plpgsql;

Dann könnten Sie Folgendes tun:

=> select array_intersect(ARRAY[2,4,6,8,10], ARRAY[1,2,3,4,5,6,7,8,9,10]);
 array_intersect 
-----------------
 {6,2,4,10,8}
(1 row)

Beachten Sie, dass dies keine bestimmte Reihenfolge im zurückgegebenen Array garantiert, aber Sie können das beheben, wenn Sie sich darum kümmern. Dann könnten Sie Ihre eigene Aggregatfunktion erstellen:

-- Pre-9.1
create aggregate array_intersect_agg(
    sfunc    = array_intersect,
    basetype = int[],
    stype    = int[],
    initcond = NULL
);

-- 9.1+ (AFAIK, I don't have 9.1 handy at the moment
-- see the comments below.
create aggregate array_intersect_agg(int[]) (
    sfunc = array_intersect,
    stype = int[]
);

Und jetzt sehen wir, warum array_intersect macht lustige und etwas klobige Sachen mit NULLen. Wir brauchen einen Anfangswert für die Aggregation, der sich wie das universelle Set verhält, und dafür können wir NULL verwenden (ja, das riecht etwas seltsam, aber mir fällt auf Anhieb nichts Besseres ein).

Sobald dies alles eingerichtet ist, können Sie Folgendes tun:

> select * from stuff;
    a    
---------
 {1,2,3}
 {1,2,3}
 {3,4,5}
(3 rows)

> select array_intersect_agg(a) from stuff;
 array_intersect_agg 
---------------------
 {3}
(1 row)

Nicht gerade einfach oder effizient, aber vielleicht ein vernünftiger Ausgangspunkt und besser als gar nichts.

Nützliche Referenzen:

  • array_agg
  • Aggregat erstellen
  • Funktion erstellen
  • PL/pgSQL
  • unnest