基本上,您可以将一个表与其自身连接以查找相似的字符串,但是这种方法将以对较大数据集的非常慢的查询告终。此外,similarity()
在某些情况下使用可能会导致不准确(您需要找到合适的限制值)。
你应该尝试寻找模式。例如,如果字符串中的所有变量词都以数字开头,则可以使用regexp_replace():
select id, regexp_replace(error, '\d\w+', 'xxxxx') as error
from errors;
id | error
----+-------------------------------------
1 | Error xxxxx, can not write to disk
2 | Error xxxxx, can not write to disk
3 | Error xxxxx, can not write to disk
4 | Fatal barier breach on object xxxxx
5 | Fatal barier breach on object xxxxx
6 | Fatal barier breach on object xxxxx
(6 rows)
因此您可以通过错误消息轻松地对数据进行分组:
select regexp_replace(error, '\d\w+', 'xxxxx') as error, count(*)
from errors
group by 1;
error | count
-------------------------------------+-------
Error xxxxx, can not write to disk | 3
Fatal barier breach on object xxxxx | 3
(2 rows)
上述查询只是一个示例,具体解决方案取决于数据格式。
使用 pg_trgm
基于 OP 想法的解决方案(请参阅下面的评论)。0.8 的限制similarity()
肯定太高了。似乎它应该在 0.6 左右。
唯一错误表(我使用了一个临时表,但它当然也是一个常规表):
create temp table if not exists unique_errors(
id serial primary key,
error text,
ids int[]);
该ids
列用于存储id
基表中包含类似错误的行。
do $$
declare
e record;
found_id int;
begin
truncate unique_errors;
for e in select * from errors loop
select min(id)
into found_id
from unique_errors u
where similarity(u.error, e.error) > 0.6;
if found_id is not null then
update unique_errors
set ids = ids || e.id
where id = found_id;
else
insert into unique_errors (error, ids)
values (e.error, array[e.id]);
end if;
end loop;
end $$;
最终结果:
select *, cardinality(ids) as count
from unique_errors;
id | error | ids | count
----+---------------------------------------+---------+-------
1 | Error 1234eee5, can not write to disk | {1,2,3} | 3
2 | Fatal barier breach on object 72fgsff | {4,5,6} | 3
(2 rows)