数组交集作为 group by 的聚合函数

Kat*_*ers 4 sql postgresql group-by aggregate-functions

我有下表:

CREATE TABLE person
AS
  SELECT name, preferences
  FROM ( VALUES
    ( 'John', ARRAY['pizza', 'meat'] ),
    ( 'John', ARRAY['pizza', 'spaghetti'] ),
    ( 'Bill', ARRAY['lettuce', 'pizza'] ),
    ( 'Bill', ARRAY['tomatoes'] )
  ) AS t(name, preferences);
Run Code Online (Sandbox Code Playgroud)

我想group by personintersect(preferences)为聚合函数。所以我想要以下输出:

person | preferences
-------------------------------
John   | ['pizza']
Bill   | []
Run Code Online (Sandbox Code Playgroud)

这应该如何在 SQL 中完成?我想我需要做类似下面的事情,但是这个X函数是什么样子的?

SELECT    person.name, array_agg(X)
FROM      person
LEFT JOIN unnest(preferences) preferences
ON        true
GROUP BY  name
Run Code Online (Sandbox Code Playgroud)

Lau*_*lbe 5

您可以创建自己的聚合函数:

CREATE OR REPLACE FUNCTION arr_sec_agg_f(anyarray, anyarray) RETURNS anyarray
   LANGUAGE sql IMMUTABLE AS
   'SELECT CASE
              WHEN $1 IS NULL
              THEN $2
              WHEN $2 IS NULL
              THEN $1
              ELSE array_agg(x)
           END
    FROM (SELECT x FROM unnest($1) a(x)
          INTERSECT
          SELECT x FROM unnest($2) a(x)
         ) q';

CREATE AGGREGATE arr_sec_agg(anyarray) (
   SFUNC = arr_sec_agg_f(anyarray, anyarray),
   STYPE = anyarray
);

SELECT name, arr_sec_agg(preferences)
FROM person
GROUP BY name;

??????????????????????
? name ? arr_sec_agg ?
??????????????????????
? John ? {pizza}     ?
? Bill ?             ?
??????????????????????
(2 rows)
Run Code Online (Sandbox Code Playgroud)


Eva*_*oll 2

FILTER与使用ARRAY_AGG

SELECT name, array_agg(pref) FILTER (WHERE namepref = total)
FROM (
  SELECT name, pref, t1.count AS total, count(*) AS namepref
  FROM (
    SELECT name, preferences, count(*) OVER (PARTITION BY name)
    FROM person
  ) AS t1
  CROSS JOIN LATERAL unnest(preferences) AS pref
  GROUP BY name, total, pref
) AS t2
GROUP BY name;
Run Code Online (Sandbox Code Playgroud)

ARRAY这是使用构造函数 和来完成此操作的一种方法DISTINCT

WITH t AS (
  SELECT name, pref, t1.count AS total, count(*) AS namepref
  FROM (
    SELECT name, preferences, count(*) OVER (PARTITION BY name)
    FROM person
  ) AS t1
  CROSS JOIN LATERAL unnest(preferences) AS pref
  GROUP BY name, total, pref
)
SELECT DISTINCT
  name,
  ARRAY(SELECT pref FROM t AS t2 WHERE total=namepref AND t.name = t2.name)
FROM t;
Run Code Online (Sandbox Code Playgroud)