私が見つけた:
しかし、私の場合に使用するのに苦労しています。
私はそのようなテーブルを持っています(実際のmyid
値はハッシュですが、ここでは説明のために簡略化しています):
create temp table a (myid text, ip inet);
insert into a (myid, ip)
values
('0a', '10.10.1.1'),
('0a', '10.10.1.2'),
('0a', '10.10.1.3'),
('0b', '10.10.1.2'),
('0b', '10.10.1.4'),
('0c', '10.10.1.5'),
('0d', '10.10.1.3'),
('0e', '10.10.1.6'),
('0e', '10.10.1.7'),
('0f', '10.10.1.8'),
('0f', '10.10.1.9'),
('10', '10.10.1.9'),
('11', '10.10.1.10'),
('12', '10.10.1.11'),
('12', '10.10.1.4'),
('1a', '10.10.1.2'),
('1a', '10.10.1.4'),
('1e', '10.10.1.11'),
('1f', '10.10.1.12'),
('23', '10.10.1.12');
私がどのように生成するかを理解できない結果は次のとおりです:
ids | ips
---------------------+------------------------------------------------------
{0a,0b,0d,12,1a,1e} | {10.10.1.1,10.10.1.2,10.10.1.3,10.10.1.4,10.10.1.11}
{0c} | {10.10.1.5}
{0e} | {10.10.1.6,10.10.1.7}
{0f,10} | {10.10.1.8,10.10.1.9}
{11} | {10.10.1.10}
{1f,23} | {10.10.1.12}
ここでのロジックは、共通のIPを持つすべてのIDがグループ化されることですtransitively。たとえば、0a
には0b
と共通のIPがあります。 0b
には12
との共通点があります。 12
は1e
と共通のものがあります。
数万行あり、特定のIDのIPの数に特定の制限はなく、特定のIPを表示できるIDの数に特定の制限はありません。
IPで集計する方法、またはIDで集計する方法を知っていますが、bothを推移的に実行すると問題が発生します。再帰的なCTEを試しましたが、正しく理解できなかったため、そもそもそれが正しいアプローチであったかどうかはわかりません。 (最初にIDでグループ化し、次に重複するIPの配列でグループ化し、集約の重複を回避できれば、すべて設定できますが、より良いアプローチがあるかもしれません。)
標準SQLで上記の結果を生成する方法はありますか?または、少なくとも標準のPostgresでは? (私は9.6.6を使用しています。)
これが失敗した試みです。 (これは結果を返すが、望ましい結果ではない正当なクエリです。)次の理由で失敗します。
n!
回返されるためです。これがクエリです:
with recursive b as (
select
array[myid] as ids,
array_agg(ip) as ips
from a
group by myid
), c as (
select
ids,
ips
from b
union
select
b.ids || c.ids,
b.ips || c.ips
from
b
join c on
(not b.ids && c.ids)
and (b.ips && c.ips)
)
select * from c
;
Group by array overlay のJack Douglasのソリューションの重要な部分の1つは、再帰的なt
CTEのような再帰的な部分の配列で使用される|
(パイプ)演算子ですこの:
...
select t.id, a.id, t.clst | a.clst
...
この演算子は、重複するアイテムを抑制する2つの配列を連結します。設定に直接回答を適用できないのは、int
配列に対してのみ|
演算子が定義されているようですが、inet
に対して同じ操作を実行する方法が必要だからです。配列。
これを行うには、配列を行セットとして扱います。気づいた場合、|
演算子が生成するのは、事実上2つのセットの和集合です。したがって、両方の配列をunnest
、それらをunion
結合し、組み合わせたセットを配列として集計すると、同じ結果が得られます。だから、この表現、
t.clst | a.clst
相関サブクエリで置き換えることができます:
(
select
array_agg(sub.n)
from
(
select unnest(t.clst)
union
select unnest(a.clst)
) as sub (n)
)
はい、置換は比較してかなり扱いにくいですが、それは仕事をします、そしてそれは最初に何かです。
ソリューションをあなたの例に適合させて(そして元のコードに少し空白を追加して)、完全なクエリは次のようになります:
with recursive
cte_a as
(
select
myid,
array_agg(distinct ip) as ip
from
a
group by
myid
)
, cte_t (myid, pmyid, ip) as
(
select
myid,
myid,
ip
from
cte_a
union all
select
t.myid,
a.myid,
( /* this is the replacement expression */
select
array_agg(sub.n)
from
(
select unnest(t.ip)
union
select unnest(a.ip)
) as sub (n)
)
from
cte_t as t
join cte_a as a
on a.myid <> t.pmyid and t.ip && a.ip and not t.ip @> a.ip
)
, cte_d as
(
select distinct on (myid)
myid,
ip
from
cte_t
order by
myid,
cardinality(ip) desc
)
select
array_agg(myid),
ip
from
cte_d
group by
ip
;
このデモ でクエリをテストできます db <> fiddle.uk。
ジャックの注意事項は、おそらくあなたの状況にも当てはまることに注意してください。
これが数百万行でうまく機能する可能性は低いことを覚えておいてください。