SQL,选择至少匹配 N 列的行对

标签 sql postgresql query-optimization

我发现自己正在寻找 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/

相关文章:

sql - 如何选择总和达到总数的 60% 的值

ruby-on-rails - 按日期对记录排序并计算重复用户的数量

postgresql - PostgreSQL 中用户交互(回复、提及)返回时间

php - 与 ID 数组相比,检测数据库中要删除的字段的最佳方法

MySQL "IN"使用子查询查询非常慢,但使用显式值查询速度很快

php - 插入数据并在列中添加前一行数据

MySQL INSERT SELECT 在大型静态​​表上

python - 如何加快MySQL更新速度

mysql - 连接两个表后如何显示5个最大值?

django - 函数内的 Postgres 咨询锁允许并发执行