【问题标题】:Postgres query for cross-checking 2 tables用于交叉检查 2 个表的 Postgres 查询
【发布时间】:2015-09-18 02:32:25
【问题描述】:

我有两个表,已经填充了如下数据:

table1 数据是公民数据,具有字段:

uniqid  (a uniqueIP for person, like social sec num) 
first_name 
last_name
birthdate
address

table2 带字段:

first_name 
last_name
birthdate
address
gender
healthinsurance_type
...

table1 数据和table2 数据来自不同的独立机构。这些表中的名称可以以不同的方式键入,等等。

table1 是名称和 ID 的权威。 table2 是我需要处理的,但没有 ID (citizenID)。

现在我需要table2 中的每一行来获取与table1 关联的citizenid,这样最后我会得到带有附加ID 列的table2,与每个人正确关联。

类似于在table1 中搜索某些条件匹配的人(table2 中的一行),如果table1 中存在记录,则将该记录的ID 放入table2 中的列中。

但要快速完成所有行。
table1 的行数约为 200 万。
table2 的行数约为 900.000

【问题讨论】:

  • 一台计算机可以非常有效地解决这个问题。基本上你不能加入那些表。也许您可以创建第三个表将两者链接在一起,或者将一个表中的列添加到另一个表中。
  • 您忘记提供数据类型和约束。使用\d tbl 提供您从 psql 获得的(相关部分)。而且,总是,你的 Postgres 版本。另外,两边可以重复吗?
  • vresion 9.1 表 1:uniqid:整数(11 位),pk 名称:char,不为空,不为空 生日:日期,不为空 table1 数据来自民事登记处。这是参考。没有重复。表 2 数据是建议的医疗保健接受者的列表,由现场官员手动收集(不幸的是,减去公民 ID),然后应提交给每个人的公民 ID 的部门。所以我需要在 table2 中的每个人上加上正确的公民 ID,使用 table1 作为参考
  • 编辑这个问题。这种信息在 cmets 中很难读取。

标签: sql postgresql pattern-matching similarity


【解决方案1】:

我假设你是唯一一个写表的人,所以没有并发冲突......

  1. 将ID列添加到table2,暂时可以为NULL:

    ALTER TABLE table2 ADD COLUMN citizenid int; -- or whatever the type is
    

    考虑在table1 上增加一个标志,以便在另一侧也以便宜的方式“退出市场”:

    ALTER TABLE table1 ADD COLUMN hasmatch boolean;
    
  2. 假设两边都没有重复。否则你需要做更多。

  3. 使用完美、完全匹配更新 table2 中的所有行。也立即标记table1 中的匹配行。用data-modifying CTE

    WITH u2 AS (
       UPDATE table2 t2
       SET    citizenid = t1.uniqid  
       FROM   table1 t1
       WHERE  t1.hasmatch IS NULL   -- always with this condition
       AND    t2.citizenid IS NULL  -- always with this condition
       AND    t2.first_name = t1.first_name
       AND    t2.last_name  = t1.last_name
       AND    t2.birthdate  = t1.birthdate
       AND    t2.address    = t1.address
       RETURNING citizenid
       )
    UPDATE table1 t1
    SET    hasmatch = TRUE
    FROM   u2
    WHERE  t2.citizenid = u2.citizenid;
    

只要一行有它的citizenid,它就会在两边都“退出市场”。

  1. 检查剩余的行数并开始在小步骤中软化谓词,始终先尝试更有可能的匹配项,从而避免误报。在开始这个循环过程之前,先考虑一个系统性策略。分析剩余的行以找到系统性拼写错误或类似线索。

字符类型列的模糊匹配的可能选项是:

lower(t2.address) = lower(t1.address)
t2.address ILIKE (t1.address || %) -- here % is a wilcard for LIKE pattern
t1.address ILIKE (t2.address || %)
lower(left(t2.address, 20)) = lower(left(t1.address, 20))
t2.address % t1.address  -- here % is the similarity operator
levenshtein(t2.address, t1.address) <= 3  -- *very* expensive, no index support

等等。 相似性运算符% 由附加模块pg_trgm 提供,该模块还提供三元索引以支持LIKE 和相似性匹配。您将需要索引进行模糊匹配,否则您的查询可能需要很长时间。

一旦找到匹配项,请考虑使用部分索引从索引中删除行。喜欢:

CREATE INDEX t1_adr_gin_trgm_idx ON table1 USING gin (address gin_trgm_ops)
WHERE t1.hasmatch IS NULL;

CREATE INDEX t2_adr_gin_trgm_idx ON table2 USING gin (address gin_trgm_ops)
WHERE t2.citizenid IS NULL;

等等

您可以使用以下方法微调% 运算符的相似度阈值:

SELECT set_limit(0.8);
  1. 一小部分将仍未解决。您可以花费越来越多的时间手动分配它们,直到您决定放弃其余部分。

  2. 可选。处理完成后,每一行都有一个citizenid,现在可以设置为NOT NULL,新行必须有一个citizenid

更多详情:

【讨论】:

  • 非常感谢,Erwin,不过在 CTE 方面还是比较新手。我可以看到方向。
  • @rifaiz:Search for [postgres] data-modifying CTE,你会发现很多例子和解释。我还添加了上面手册的链接。
猜你喜欢
  • 1970-01-01
  • 2019-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
  • 2014-10-23
相关资源
最近更新 更多