用于交叉检查 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
我假设你是唯一一个写入表的人,所以没有并发冲突......
将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 中的所有行。也立即标记 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"。
- 检查还剩多少行并开始逐步软化谓词以始终首先尝试更有可能的匹配来防止误报。在开始此 循环过程 之前,请考虑一个 系统策略 。分析剩余行以查找系统拼写错误或类似线索。
字符类型列的模糊匹配的可能选项是:
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);
一小部分问题仍未解决。您可以花费越来越多的时间来手动分配它们,直到您决定放弃其余部分。
可选。处理完成后,每一行都有一个 citizenid
,现在可以设置为 NOT NULL
,新行必须有一个 citizenid
更多详情:
- Finding similar strings with PostgreSQL quickly
- Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL
- How to create simple fuzzy search with Postgresql only?
我有两个表,已经填充了如下数据:
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
我假设你是唯一一个写入表的人,所以没有并发冲突......
将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 中的所有行。也立即标记
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"。
- 检查还剩多少行并开始逐步软化谓词以始终首先尝试更有可能的匹配来防止误报。在开始此 循环过程 之前,请考虑一个 系统策略 。分析剩余行以查找系统拼写错误或类似线索。
字符类型列的模糊匹配的可能选项是:
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);
一小部分问题仍未解决。您可以花费越来越多的时间来手动分配它们,直到您决定放弃其余部分。
可选。处理完成后,每一行都有一个
citizenid
,现在可以设置为NOT NULL
,新行必须有一个citizenid
更多详情:
- Finding similar strings with PostgreSQL quickly
- Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL
- How to create simple fuzzy search with Postgresql only?