【问题标题】:Postgres find similar rows between two tablesPostgres 在两个表之间找到相似的行
【发布时间】:2018-09-30 12:18:01
【问题描述】:

我试图找出不同表中的行之间的相似性。这是 DDL。

CREATE TABLE a 
(
    id int, 
    fname text, 
    lname text, 
    email text, 
    phone text
);

INSERT INTO a 
VALUES (1, 'john', 'doe', 'john@gmail.com', null), 
       (2, 'peter', 'green', 'peter@gmail.com', null);

CREATE TABLE b 
(
    id int, 
    fname text, 
    lname text, 
    email text, 
    phone text
);

INSERT INTO b 
VALUES (null, 'peter', 'glover', 'bob@gmail.com', '777'),
       (null, null, 'green', 'peter@gmail.com', '666');

假设我们有以下相似性配置

fname = 0.1
lname = 0.3
email = 0.5
phone = 0.5

所以我们可以说两者之间的相似性

(2, 'peter', 'green', 'peter@gmail.com', null) and
(null, null, 'green', 'peter@gmail.com', '666') is 0.8 (lname + email)

(2, 'peter', 'green', 'peter@gmail.com', null) and
(null, 'peter', 'glover', 'bob@gmail.com', '777') is 0.1 (fname)

因此,我希望从表 b 中获取与表 a 相似度超过某个阈值(假设为 0.7)的数据。所以根据例子,我需要得到这样的东西

id, fname, lname, email, phone, similarity
2,  null,'green', 'peter@gmail.com', '666', 0.8

其中 id 是表 a 中相似行的 id

我已经尝试过 NATURAL FULL OUTER JOIN 和 EXCEPT,但它不适合我的目的,或者我只是做错了什么。

还有什么样的索引适合查询?因为表 a 可能有十亿行。

更新

目标是匹配行。那么可能会更好地将所有信息存储在一个表中并执行窗口功能?逻辑会一样,靠相似配置

id | fname | lname  |      email      | phone 
---+-------+--------+-----------------+-------
 1 | john  | doe    | john@gmail.com  | 
 2 | peter | green  | peter@gmail.com |
   | peter | glover | bob@gmail.com   | 777
   |       | green  | peter@gmail.com | 666 

对一些id为null的操作行后,填充id相似度最高且大于0.7的行,否则生成一个新的

【问题讨论】:

    标签: database postgresql similarity


    【解决方案1】:
    -- get similarity betweena and b tables
    with with_similarity as (
    select 
    a.id, b.id as tmp_id, b.fname, b.lname, b.email, b.phone,
    ( coalesce((a.fname = b.fname)::int, 0) * 0.1 +
            coalesce((a.lname = b.lname)::int, 0) * 0.3 +
            coalesce((a.email = b.email)::int, 0) * 0.5 +
            coalesce((a.phone = b.phone)::int, 0) * 0.5
    ) as similarity
    from b
    cross join a
    ), 
    -- as we have matched weight for all rows, we can pickup rank them
    matched as (
    select *,
    ROW_NUMBER() OVER(PARTITION BY tmp_id ORDER BY similarity DESC) AS rk
    from with_similarity
    )
    
    -- pick up best match and insert matched + not matched rows
    select id, fname, lname, email, phone from matched where rk=1 and similarity >= 0.7
    union all
    select tmp_id, fname, lname, email, phone from matched where similarity < 0.7 and rk = 1;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-16
      • 2019-03-08
      • 2016-12-14
      相关资源
      最近更新 更多