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 person与intersect(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)
您可以创建自己的聚合函数:
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)
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)