我发现自己正在寻找 SQL 的异常问题。
假设我有一个包含 N 列的表 T1,为简单起见命名为 A...Z。
我需要的是找到至少匹配 N 个属性的所有行对。
让我们看一个非常简单的例子:
ID | A | B | C | D |
---+---+---+---+---+-
-1-| 1 | 2 | 3 | 4 |
---+---+---+---+---+-
-2-| 2 | 3 | 4 | 1 |
---+---+---+---+---+-
-3-| 1 | 2 | 2 | 1 |
在这种情况下,N = 2
- 第 1 行应与第 3 行匹配(它们在 A 和 B 列上匹配)
- 第 2 行没有匹配项
- 第 3 行与第 1 行匹配,因为关系是对称的。
您知道如何处理这个问题吗?
最佳答案
在表上使用自连接,您可以找到所有行对的匹配值的数量:
with my_table(id, a, b, c, d) as (
values
(1, 1, 2, 3, 4),
(2, 2, 3, 4, 1),
(3, 1, 2, 2, 1)
)
select
t1.id, t2.id,
(t1.a = t2.a)::int+ (t1.b = t2.b)::int+ (t1.c = t2.c)::int+ (t1.d = t2.d)::int as matches
from my_table t1
join my_table t2 on t1.id < t2.id
id | id | matches
----+----+---------
1 | 2 | 0
1 | 3 | 2
2 | 3 | 1
(3 rows)
如果列数未知,您可以转换表格以获取数组而不是单列:
with my_table(id, a, b, c, d) as (
values
(1, 1, 2, 3, 4),
(2, 2, 3, 4, 1),
(3, 1, 2, 2, 1)
),
my_table_transformed (id, cols) as (
select id, array_agg(value::int)
from my_table,
to_jsonb(my_table) j,
jsonb_each_text(j)
where key <> 'id'
group by 1
)
select *
from my_table_transformed t1
id | cols
----+-----------
1 | {1,2,3,4}
2 | {2,3,4,1}
3 | {1,2,2,1}
(3 rows)
现在您需要一个函数来获取两个数组中的多个匹配值:
create or replace function find_matches(a1 int[], a2 int[])
returns int language sql as $$
select sum(m)::int
from (
select (c1 = c2)::int as m
from unnest(a1, a2) u(c1, c2)
) s
$$;
并使用转换表的函数:
with my_table(id, a, b, c, d) as (
values
(1, 1, 2, 3, 4),
(2, 2, 3, 4, 1),
(3, 1, 2, 2, 1)
),
my_table_transformed (id, cols) as (
select id, array_agg(value::int)
from my_table,
to_jsonb(my_table) j,
jsonb_each_text(j)
where key <> 'id'
group by 1
)
select t1.id, t2.id, find_matches(t1.cols, t2.cols)
from my_table_transformed t1
join my_table_transformed t2 on t1.id < t2.id;
id | id | find_matches
----+----+--------------
1 | 2 | 0
1 | 3 | 2
2 | 3 | 1
(3 rows)
最后一个查询适用于具有不同列数的表。
关于SQL,选择至少匹配 N 列的行对,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44064696/