1

我有一个 pl/pgsql 脚本,它需要检查一个单词/句子是否在一个字符串中,它必须注意单词边界,并且不区分大小写。

例子:

  • 字符串:“我的标签 xx zz yy”,模式:“我的标签”,MATCH
  • 字符串:“xx 我的标签 zz”,模式:“我的标签”,MATCH
  • 字符串:“我的标签xx zz”,模式:“我的标签”,不匹配

所以显而易见的解决方案是使用正则表达式,如下所示:

select _label ~* (E'\\y' || _pattern || E'\\y') into _match;

与简单的相比,它可以工作但速度很慢

select _label ilike '%' || _pattern || '%' into _match;

这包含在一个函数中,我的脚本调用了 A LOT(数千万,我做了很多递归),并且由于这个要求,整体运行时间翻了一番。

现在我的问题是,有没有更快的方法来实现这个?

谢谢。

编辑:最终使用了这个:

if _label ilike '%' || _pattern || '%' then
    select _label ~* (E'\\m' || _pattern || E'\\M') into _match;
end if;

而且速度明显更快。

4

1 回答 1

2

我会考虑全文搜索功能,但根据您的描述,我可能会使用 PostgreSQL arrays来实现这一点。

首先:定义一个函数,它接受一个标签,将其小写(如果您愿意,也可以大写),在单词边界上拆分它,然后返回一个数组。说:

CREATE OR REPLACE FUNCTION label_to_array(text) RETURNS text[] AS $$
SELECT regexp_split_to_array(lower($1), E'\\W');
$$ LANGUAGE sql IMMUTABLE;

$ select label_to_array('my label xx zz yy');
   label_to_array    
---------------------
 {my,label,xx,zz,yy}

现在,在这个函数上创建一个GIN 索引:

CREATE INDEX sometable_label_array_key ON sometable
 USING GIN((label_to_array(label));

从这里开始,PostgreSQL 可以将此索引用于许多涉及数组运算符的查询,例如“包含”:

SELECT *
FROM sometable
WHERE label_to_array(label) @> label_to_array('my label');

此查询将拆分'my label'{my,label},然后使用索引查找包含 的行列表,my将其与包含 的行列表相交,label然后返回结果。这并不完全等同于您的原始查询(因为它不检查它们的顺序),但由于它使用索引来消除表中的大部分行,所以在最后添加原始检查就可以了:

SELECT *
FROM sometable
WHERE label_to_array(label) <@ label_to_array('my label')
AND label ~* (E'\\y' || 'my label' || E'\\y');
于 2012-11-05T15:32:33.973 回答