Postgres 通过组合进行聚合

问题描述 投票:0回答:1

我正在尝试构建一个查询,该查询将为每个人提供每个可能的源集的模态结果。

考虑我有下表:

| ID       | Source   | First_Name |
| -------- | -------- | -----———-- |
| 1        | A        | John       |
| 1        | A        | John       |
| 1        | B        | James      |
| 1        | B        | Jim        |
| 1        | B        | Jim        |
| 1        | B        | Jim        |
| 1        | C        | John       |
| 1        | C        | John       |

我希望查询返回以下内容:

| ID       | Sources  | First Name |
| -------- | -------- | -----———-- |
| 1        | {A}      | John       |
| 1        | {B}      | Jim        |
| 1        | {C}      | John       |
| 1        | {A,B}    | Jim        |
| 1        | {B,C}    | Jim        |
| 1        | {A,C}    | John       |
| 1        | {A,B,C}  | John       |

我尝试了一些递归查询,但收效甚微,只能找到一组静态的解决方案 来源,我想动态重复。

即我可以执行以下操作,但它既不干净、动态也不可扩展:

SELECT id
      ,'{A}'
      ,MODE() WITHIN GROUP (ORDER BY first_name) first_name
FROM   person
WHERE  source = 'A'
GROUP BY id
UNION ALL
SELECT id
      ,'{B}'
      ,MODE() WITHIN GROUP (ORDER BY first_name) first_name
FROM   person
WHERE  source = 'B'
GROUP BY id
UNION ALL
SELECT id
      ,'{C}'
      ,MODE() WITHIN GROUP (ORDER BY first_name) first_name
FROM   person
WHERE  source = 'C'
GROUP BY id
UNION ALL
SELECT id
      ,'{A,B}'
      ,MODE() WITHIN GROUP (ORDER BY first_name) first_name
FROM   person
WHERE  source IN ('A','B')
GROUP BY id
UNION ALL
SELECT id
      ,'{B,C}'
      ,MODE() WITHIN GROUP (ORDER BY first_name) first_name
FROM   person
WHERE  source IN ('B','C')
GROUP BY id
UNION ALL
SELECT id
      ,'{A,C}'
      ,MODE() WITHIN GROUP (ORDER BY first_name) first_name
FROM   person
WHERE  source IN ('A','C')
GROUP BY id
UNION ALL
SELECT id
      ,'{A,B,C}'
      ,MODE() WITHIN GROUP (ORDER BY first_name) first_name
FROM   person
WHERE  source IN ('A','B','C')
GROUP BY id

注意:我只包含 ID 1 作为示例,该表每个 ID 有多行

sql postgresql aggregate-functions
1个回答
0
投票

为了简化您的工作,请将任务分为两部分。首先,找到源的所有组合。使用递归查询,为了清晰起见,将其定义为函数:

create or replace function combinations(anyarray)
returns setof anyarray language sql as $$
    with recursive combinations as (
        with elements as (
            select
                $1[idx] as elem,
                idx, 1 as len
            from generate_series(1, cardinality($1)) as idx
            )
        select 
            array[elem] as elems,
            idx, len
        from elements
    union all
        select 
            e.elem || c.elems,
            e.idx, c.len+ 1
        from combinations c
        join elements e on c.idx > e.idx
        )
    select elems
    from combinations
    order by len, elems
$$;

有了这个功能,你的主要查询就比较简单了:

select 
    id, 
    sources, 
    mode() within group (order by first_name) as first_name
from person
cross join (
    select combinations(array_agg(distinct source)) as sources
    from person
) combinations
where source = any(sources)
group by id, sources
order by cardinality(sources), sources

db<>fiddle 中测试它。

© www.soinside.com 2019 - 2024. All rights reserved.