用于交叉检查 2 个表的 Postgres 查询

Postgres query for cross-checking 2 tables

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

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 中的每一行都得到一个 citizenid,与 table1 相关联,所以最后我得到带有附加 ID 列的 table2,与每个人正确关联。

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

但是要对所有行进行快速处理。
table1 的行数约为 200 万。
table2 的行数约为 900.000

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

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

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

    考虑在 table1 上设置一个额外的标志,以便在另一侧以便宜的方式获取行 "off the market":

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

  3. 完全匹配更新表 2 中的所有行。也立即标记 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,两边都是 "off the market"。

  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 和相似性匹配。您将需要 个索引 进行模糊匹配,否则您的查询可能需要 时间。

  • PostgreSQL LIKE query performance variations

考虑部分索引,以便在找到匹配项后立即从索引中删除行。喜欢:

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

更多详情: