我自己在寻找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
你对如何处理这个有什么想法吗?
答案 0 :(得分:1)
我建议这样的事情。当然可以根据请求在案例列表中添加字段数:
select id1, id2, matches from
(
select
tab1.id id1, tab2.id id2,
(case when tab1.a=tab2.a then 1 else 0 end)+
(case when tab2.b=tab2.b then 1 else 0 end) matches
from
t1 tab1
cross join t1 tab2
)
where matches>1;
您将获得匹配行的ID以及匹配字段的数量。
答案 1 :(得分: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)
最后一个查询适用于具有不同列数的表。