SQL Group由类似的字段值组成

时间:2014-02-07 16:03:25

标签: sql ruby-on-rails postgresql pattern-matching

我想知道如何以一种好的方式解决以下问题。因此,我们有一个包含数千个与特定关键字对应的记录的表。由于我们的应用程序上有自定义DSL,我们最终会得到几个“相似”的值,如:

office equipment
[office equipment]
+office equipment
office -equipment

我需要做的是对所有这些值进行分组,并提供一种总结的“办公设备4”,参考上面的例子。

我可以通过使用一些比较算法在代码上完成它,但这意味着将数千个对象检索到内存中。有没有办法使用SQL实现这些?

2 个答案:

答案 0 :(得分:1)

在基本表格上匹配

例如,将所有非单词字符替换为regexp_replace()并折叠为小写:

SELECT lower(regexp_replace (keyword, '\W', '', 'g')) AS folded_key
     , count(*) AS ct
FROM   tbl
GROUP  BY 1;

我使用class shorthand \W来识别由基础语言环境的ctype定义的非字符字符的所有字符。这应该删除所有无关紧要的字符。铸造到小写是折叠中的另一个(可选的)步骤。您可能需要应用更多/其他string functions来获取所需的基本表单。

unaccent()

“字符串规范化”的另一个有用方法是用unaccent()从词位中删除变音符号。完整的详细信息:
Does PostgreSQL support "accent insensitive" collations?

匹配基本关键字列表

从长远来看,您应该创建一个查找表,列出所有基本关键字并使用主表中的外键约束。想想database normalization

CREATE TABLE keyword
  keyword_id serial PRIMARY KEY
 ,keyword text UNIQUE
);

这种查找表在任何情况下都是有用的,即使没有参照完整性(外键约束)。定义基本术语后,您可以按相似性进行分组,这将比上面的“强力”方法提供更好的结果。它不再是黑暗中的刺。现在,Postgres知道将类似条目组合在一起的位置。

附加模块fuzzystrmatch中有几个可能有用的运算符/函数。但我的第一选择是模块similarity operator %提供的pg_trgm。使用这些运算符,您甚至可以容忍拼写错误和语法变化。查询可能如下所示:

-- SELECT set_limit(0.7); -- optional; see below.
SELECT k.keyword, count(*) AS ct
FROM   keyword_tbl k
LEFT   JOIN tbl    t ON k.keyword % t.keyword
GROUP  BY 1;

您可以使用提供的功能set_limit()校准匹配的容差。

LEFT JOIN包含不匹配的关键字。你可能想要也可能不想要那样。如果不这样做,请替换为JOIN

顶部的樱桃:相似性运算符%可以使用GIN或GiST索引,这使得大表的更多更快。这个相关答案的细节:
PostgreSQL LIKE query performance variations

结合两者

如果仍然不够准确,你可以将两种方法结合起来:与“标准化”字符串的相似性:

SELECT k.keyword, count(*) AS ct
FROM   keyword_tbl k
LEFT   JOIN tbl    t ON k.keyword
                      % lower(regexp_replace (t.keyword, '\W', '', 'g'))
GROUP  BY 1;

您甚至可以使用功能性GiST索引支持该功能。这里的例子:
Postgresql: Matching Patterns between Two Columns

答案 1 :(得分:0)

嗯,对于你给出的例子:

select translate(col, '[]+-()', '') as newval, count(*)
from table t
group by 1;

translate()只删除似乎不属于的字符。