我正在尝试构建一个查询,该查询将为每个人提供每个可能的源集的模态结果。
考虑我有下表:
| 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 有多行
为了简化您的工作,请将任务分为两部分。首先,找到源的所有组合。使用递归查询,为了清晰起见,将其定义为函数:
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