成对计数共享另一个字段的相同值到 SQL 中的邻接列表中

问题描述

我有一张看起来像这样的桌子

| id | name  |
|  A | Fred  |
|  A | Steve |
|  B | Al    |
|  B | Fred  |
|  B | Jim   |
|  C | Steve |
|  C | Jim   |

我正在寻找一个查询,该查询可以按所有对共享相同 id 的名称对聚合 id 的计数。

也就是说,我想要的查询变成了:

| name1 | name2 | value |
| Al    | Al    |     1 |
| Al    | Jim   |     1 |
| Al    | Fred  |     1 |
| Al    | Steve |     0 |
| Fred  | Fred  |     2 |
| Fred  | Jim   |     1 |
| Fred  | Steve |     1 |
| Jim   | Jim   |     2 |
| Jim   | Steve |     1 |
| Steve | Steve |     2 |

如果我数对了,我认为是对的。

值得注意:

  • self-self 是该名称的总数,并且
  • 也是 AB = BA 并且不重复。
  • 0,其中 Name1 和 Name2 没有共同的 ID

我的两个问题是:

  1. 如果我想用谷歌搜索这个,这种操作叫什么?这是交叉表吗?某种支点?
  2. 我该怎么做?
  3. (额外的功劳,不仅适用于所有对,我将如何概括它以适用于所有三元组?)

这是我的 db-fiddle 开始:

https://www.db-fiddle.com/f/fXffVUHyhTTBGrJRrvYPrx/0

作为:

create table People (
  id text,name text
  );
  
insert into People( 
  id,name) VALUES 
    ('A','Fred'),('A','Steve'),('B','Al'),'Jim'),('C','Jim');
   
SELECT  a.name as name1,b.name as name2,count(*) as value
   FROM People a
   JOIN People b on a.id = b.id
   WHERE a.name <= b.name
   group by name1,name2;

不幸的是,这会产生:

| name1 | name2 | value |
| ----- | ----- | ----- |
| Al    | Al    | 1     |
| Al    | Fred  | 1     |
| Al    | Jim   | 1     |
| Fred  | Fred  | 2     |
| Fred  | Jim   | 1     |
| Fred  | Steve | 1     |
| Jim   | Jim   | 2     |
| Jim   | Steve | 1     |
| Steve | Steve | 2     |

这不是我想要的,因为它缺少零。

解决方法

demo:db<>fiddle

SELECT
    p1.name,p2.name,COUNT(*) FILTER (WHERE p1.id = p2.id)      -- 2
FROM people p1
    LEFT JOIN people p2 ON p1.name <= p2.name  -- 1
GROUP BY p1.name,p2.name
  1. 自连接以将所有名称与所有其他名称进行匹配(这也是为了获得 AlSteve 的匹配行);为了避免双重配对(AB 和 BA),连接条件是 <=
  2. 计算所有出现的对,但只计算 id 匹配的那些。如果在连接条件中执行此操作,则会消除 Al/Steve 配对,现在与 COUNT = 0 一起出现。
,

您可以按如下方式使用 distinct 名称和 LEFT JOIN

with all_people as (select distinct name from people) 
select a.name,b.name,count(case when pa.id = pb.id then 1 end)
  from all_people a
  join all_people b on a.name <= b.name
  left join people pa on a.name = pa.name
  left join people pb on b.name = pb.name
group by a.name,b.name
order by a.name

Demo