PostgreSQL:如何从标签数组生成边缘列表?

时间:2014-02-28 20:57:21

标签: sql postgresql

我有一些如下所示的数据:

id, tags
1,{'A', 'B', 'C', 'D'}
2,{'A', 'C', 'D'}
3,{'A'}
4,{'B', 'D'}
5,{'A', 'D'}
6,{'D'}
7,{'D'}

我的目标是将其转换为边缘列表(或共现)表,如下所示:

tag1,tag2,count
'A', 'A', 1
'A', 'B', 1
'A', 'c', 2
'A', 'D', 3
'B', 'C', 1
'B', 'D', 2
'C', 'D', 2
'D', 'D', 2

注意第一个&上表中的最后一行('A', 'A', 1& 'D', 'D', 2)是因为A只出现在其中,因为D出现两次 - 所以它们是自连接的。

如何使用PostgreSQL 9.3有效地完成此操作?我有超过350K的标签& 190万份文件。

示例数据:

create table tags( 
id int
,tagList text[]
);

insert into tags values (1,ARRAY['A', 'B', 'C', 'D']);
insert into tags values (2,ARRAY['A', 'C', 'D']);
insert into tags values (3,ARRAY['A']);
insert into tags values (4,ARRAY['B', 'D']);
insert into tags values (5,ARRAY['A', 'D']);
insert into tags values (6,ARRAY['D']);
insert into tags values (7,ARRAY['D']);

我尝试了什么:

select a.tag, b.tag, count(*)
from
(select id, unnest(taglist) as tag
from  tags
) as a
inner join 
(select id, unnest(taglist) as tag
from  tags
) as b
on a.id = b.id and a.tag !=b.tag
group by a.tag, b.tag
order by a.tag, b.tag

产生:

tag tag count
A   B   1
A   C   2
A   D   3
B   A   1
B   C   1
B   D   2
C   A   2
C   B   1
C   D   2
D   A   3
D   B   2
D   C   2

上表中缺少的是:它考虑A->B& B->A独立 - 我不希望这种情况发生(我认为这里的术语是,我正在使用无向图),另一个是:它缺少自连接顶点。即'A< - > A'& 'D< - > D' - 我想这是因为连接语句中的a.tag!=b.tag条件。

SQL Fiddle Demo

PS:我的数据集也很长,即每行一个标签,因此每个文档(id)​​可以分布在很多行上。

2 个答案:

答案 0 :(得分:3)

SQL Fiddle

with s as (
    select
        id,
        unnest(taglist) as tag,
        array_length(taglist, 1) as l
    from  tags
)
select a.tag as tag1, b.tag as tag2, count(*)
from
    s a
    inner join
    s b on
        a.id = b.id
        and
        (
            a.tag < b.tag
            or
            (
                a.tag = b.tag
                and
                1 = all(array[a.l, b.l])
            )
        )
group by a.tag, b.tag
order by a.tag, b.tag
;
 tag1 | tag2 | count 
------+------+-------
 A    | A    |     1
 A    | B    |     1
 A    | C    |     2
 A    | D    |     3
 B    | C    |     1
 B    | D    |     2
 C    | D    |     2
 D    | D    |     2

答案 1 :(得分:2)

只需将a.tag != b.tag更改为a.tag < b.tag,您就可以获得与相同的结果自连边数除外。实际上,A与B,C,D相比较; B得分与C和D相比; C得与D比较;并且没有标签与自身相比。

完整的查询如下所示:

select a.tag, b.tag, count(*)
from
(select id, unnest(taglist) as tag from  tags) as a
inner join 
(select id, unnest(taglist) as tag from  tags) as b
on a.id = b.id and a.tag < b.tag
group by a.tag, b.tag
order by a.tag, b.tag