从另一个表中选择列中的相似值并在主表中使用另一个表值

Posted

技术标签:

【中文标题】从另一个表中选择列中的相似值并在主表中使用另一个表值【英文标题】:Select the similar value in column from another table and use another table value in main table 【发布时间】:2019-10-02 01:15:46 【问题描述】:

我有一张桌子,所有商店的名称如下:

表 1

id|name
1 |wairau road

在我的第二个表 2 中,我有以下值:

表 2

id|name        |customer_name
 1|wairau rd   |shelly
 2|wairau road |andy
 3|wairauroad  |ally

当我做select * from table 2时,

我的预期输出如下:

身份证|姓名 |客户姓名 1|怀劳路|贝壳 2|怀劳路|安迪 3|怀劳路|盟友

注意到名称现在与 table1 中的名称同步。有没有办法在 postgres/redshift SQL 中做到这一点?

【问题讨论】:

您确实需要修复数据。这对于查询来说可能非常棘手。 如果您的Table 2 有一个指向Table 1 的外键,那么可以。添加它,一个简单的连接就可以解决问题。 @TheImpaler,不幸的是,两个表上都没有外键。它只是两个表上的普通表 那么,数据库引擎无法猜测商店的名称。 @TheImpaler 谢谢,我想我会与供应商核实,然后修复数据 【参考方案1】:

正如您已经听说的,脏数据的解决方案是清理它。如果您正在处理显示的地址数据,地址标准化是一个很好的主题,可以自行研究。这是一个常见的问题,有很多解决方案和服务。最佳方法很大程度上取决于您需要支持哪些国家/地区以及您的预算。

您已经提出了使用 soundex 来帮助解决此问题的好建议。 soundex 所做的是将字符串转换为代码,以便发音相似的字符串转换为相同的代码。这种翻译可以提前准备好并存储在索引中,这样可以非常快速地进行 soundex 比较。不好的一面是,soundex 是一百年前的,用于对姓氏评分,并且是为美式英语版本而设置的。因此,这并不是解决所有问题的好方法。你会在同一个扩展中找到 Meataphone,它可能会好一点,但或多或​​少都有相同的优点和缺点。该扩展还具有 Levenshtein 距离,也称为“编辑距离”。它计算将一个字符串转换为另一个字符串需要进行多少更改。较长的字符串比非常短的字符串更好。这很棒!但这也不是您可以预先计算的东西,因为您不知道要将其与什么进行比较。但是,一旦您通过其他方式找到了一些可能的匹配项,它是对相似字符串进行排名的好工具。

说到其他方式,Postgres 中还有另一个出色的工具可供查看:

https://www.postgresql.org/docs/current/pgtrgm.html

它是标准软件包的一部分,因此您可以按照您已经获得的用于模糊字符串匹配的说明安装它。 Trigrams 是一个超出上面列出的简单模糊字符串匹配的世界。他们背后有大量的研究,在不同的语言和数据集上都能很好地工作,很棒的东西。上周我终于花了一些时间查看 Postgres 的实现,它很棒

select 'wairau rd' as address, show_trgm('wairau rd')   union all
select 'wairau rd' as address, show_trgm('wairau road') union all
select 'wairau rd' as address, show_trgm('wairauroad')

这会吐出这样的东西:

address,show_trgm
wairau rd,"""  r"",""  w"","" rd"","" wa"",air,""au "",ira,rau,""rd "",wai"
wairau rd,"""  r"",""  w"","" ro"","" wa"",""ad "",air,""au "",ira,oad,rau,roa,wai"
wairau rd,"""  w"","" wa"",""ad "",air,aur,ira,oad,rau,roa,uro,wai"

神奇的部分是 Postgres 然后可以使用这些块进行一些非常好的比较和猜测与索引。由于索引位于覆盖整个字符串的这些小块上,因此您摆脱了标准 B 树的左锚限制。这提供了很大的灵活性和强大的功能,而不会使查询变慢。

有两种设置索引的方法,具体取决于您感兴趣的比较类型。也需要在时间和空间上进行权衡,但是一旦您确定此工具适用,就需要检查一下甚至是你的好搭档。这是两个索引:

CREATE INDEX table_1_names_gin
    ON table1
    USING gin (address gin_trgm_ops);

CREATE INDEX table_1_names_gist
    ON table1
    USING gist (address gist_trgm_ops);

我在这里将您的字段称为“地址”,因为我不会调用字段名称。一旦有了这些索引,就可以进行快速的 LIKE 或 ILIKE 搜索,以及模式匹配搜索,而无需复杂的正则表达式语法。像这样(未经测试)的开始搜索:

select * 
  from table2
 where address ILIKE 'wairu%'

甚至这个包含搜索:

select * 
  from table2
 where address ILIKE 'wairu%'

或者这用于相似性搜索: 来自分析扫描

select * 
  from table2
 where address %> 'wairu'

还有很多,但我会停下来。而且,老实说,无论如何,地址标准化都应该是您的第一步。但模糊匹配会有所帮助。

提示:过去,我发现人们输入错误或不一致的地址通常有一些好的(可用性/UX)原因。如果您的数据库是公司应用程序的一部分,则一种选择是每晚运行一份报告,查找并标记看起来不标准的地址或名称。模糊匹配在这里非常有用。然后,有人可以帮助培训犯错的人做得更好。或者您发现系统的 UI 使输入坏数据比输入好数据更容易。在这种情况下,您可以重新设计应用程序以使其变得更好,并衡量几乎重复的变化,以衡量您的表现如何。

【讨论】:

感谢您的想法和评论!会看看它:)【参考方案2】:

您可以使用 soundex(在扩展模糊字符串匹配中)。在the SQL Fiddle中测试

CREATE EXTENSION if not exists fuzzystrmatch;
CREATE TABLE t1 (id int, name text);
CREATE TABLE t2 (id int, name text, customer_name text);

INSERT INTO t1 VALUES (1, 'wairau road');
INSERT INTO t1 VALUES (2, 'joe road');
INSERT INTO t1 VALUES (3, 'jerry road');
INSERT INTO t2 VALUES (1, 'wairau rd', 'shelly');
INSERT INTO t2 VALUES (2, 'wairau road', 'andy');
INSERT INTO t2 VALUES (3, 'wairauroad', 'ally');
INSERT INTO t2 VALUES (4, 'joe  row', 'john');
INSERT INTO t2 VALUES (5, 'joe.rd', 'jack');

SELECT DISTINCT ON (t2.id) t2.id, t1.name, t2.customer_name,
                           t2.name AS data_entry_name
FROM t2
CROSS JOIN t1
ORDER BY t2.id, t1.name = t2.name DESC, difference(t1.name, t2.name) DESC, t1.name

如果有大量数据,查询可能会很慢。它通过 ORDER BY 选择最可能的匹配,在这种情况下,按顺序:

    如果名称完全相同 如果名称相似(1=不相似.. 4=非常相似) 表 1 中名称的字母顺序

您可以添加更多规则,例如如果没有空格的小写版本匹配。

这个查询的问题是它会显示最可能的匹配,如果没有可能的匹配,这可能是完全错误的。此外,这是一个最佳猜测,因此查询可能会做出错误的选择。

【讨论】:

以上是关于从另一个表中选择列中的相似值并在主表中使用另一个表值的主要内容,如果未能解决你的问题,请参考以下文章

从另一个表中选择多个值并放入一个由 - 分割的字符串

如何使用第一个表中的逗号分隔值从另一个表中选择行? [复制]

我想将表单中的数据插入到表中,然后从另一个表中选择另一个数据并使用 PHP 插入到第三个表中

如何根据从另一个表中选择的结果更新一个表中的列

MYSQL从另一个表更新数据,如果存在,否则插入[重复]

使用外键关系更新从另一个表中选择的电子邮件