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

时间:2019-10-02 01:15:46

标签: sql database postgresql amazon-redshift

我有一个table1,其中所有商店名称如下:

表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时,

我的预期输出如下:

id |名称| customer_name     1 | Wairau道路| Shelly     2 | Wairau Road |安迪     威乐路3号|盟友

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

2 个答案:

答案 0 :(得分:2)

如您所知,脏数据的解决方案是清理它。如果您要像显示的那样处理地址数据,那么地址标准化本身就是一个不错的选择。这是一个普遍的问题,有很多解决方案和服务。最佳方法可能在很大程度上取决于您需要支持的国家和预算。

您已经很好地建议使用soundex来解决此问题。 soundex的作用是将字符串转换为代码,以便将听起来相似的字符串转换为相同的代码。可以预先准备好此翻译,并将其存储在索引中,这可以使音效比较非常快。不好的一面是,soundex的历史可追溯到一百年前,它是用来给姓氏打分的,并且是为美国英语版本而设置的。因此,并不是每个问题都适合。您会在同一扩展名中找到Meataphone,它可能会更好一些,但或多或​​少都具有相同的优缺点。该扩展名也具有Levenshtein距离,也称为“编辑距离”。它计算将一个字符串转换为另一字符串必须进行的更改。长字符串比短字符串更好。这很棒!但这也不是您可以预先计算的,因为您不知道要与之比较。但是,一旦通过其他方法找到了一些可能的匹配项,它便是对相似字符串进行排名的好工具。

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

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

这是标准软件包的一部分,因此您将按照与模糊字符串匹配相同的说明安装它。 Trigram是一个超越上面列出的简单模糊字符串匹配的世界。他们背后进行了大量研究,可以在不同的语言和数据集之间很好地工作。上周,我终于花了一些时间看一下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%'

或者通过以下类似搜索: 来自analytic_scan

select * 
  from table2
 where address %> 'wairu'

还有更多,但我会停下来。老实说,无论如何,地址标准化应该是您的第一步。但是模糊匹配可以提供帮助。

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

答案 1 :(得分:1)

您可以使用soundex(扩展名为Fuzzystrmatch)。在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. 如果名称完全相同
  2. 如果名称相似(1 =不相似.. 4 =非常相似)
  3. 表1中名称的字母顺序

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

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