如何搜索Postgres中的字符串中是否存在整个单词

时间:2016-07-22 08:28:35

标签: sql postgresql

我有一个列field的表格,其值类似于三星手机。

我的问题是如果我搜索字符串“Samsung”或“phone”,我怎么能得到这一行。如果我只将“Sam”或“ph”作为搜索词,我不想要任何结果。

我曾尝试使用ILIKE运算符但是如果我使用:

select * 
from mytable 
where title ILIKE 'Samsung';

它没有给出任何结果,因为它认为标题应该完全相同。此外,如果我使用title ILIKE '%Samsung%',那么它将适用于任何部分字符串。

简而言之,我想如果输入搜索词在我的列中作为整个单词存在,那么只给出搜索结果。

此外,如果我有多个搜索词值,那么任何词匹配都应该是结果

3 个答案:

答案 0 :(得分:3)

您可以使用正则表达式:

where title ~* '(\mphone\M)|(\msamsung\M)'

上述内容仅返回phonesamsung为完整字词的值。正则表达式修饰符\m\M使模式仅匹配整个单词。

正则表达式运算符~*使此不区分大小写。上述表达式将返回Samsung PhoneGoogle Phone,但不返回Sam's House

如果您想添加更多字词,只需使用“或”运算符|

添加它们即可
where title ~* '(\mphone\M)|(\msamsung\M)|(\mbhat\M)'

请注意,这种搜索不会超快。正则表达式很昂贵,无法使用任何索引。

答案 1 :(得分:0)

您也可以使用此功能:

title ~* '(^|[^\w])samsung([^\w]|$)'

以上代码的优点是,可以很容易地扩展它以包括来自以下不同编码的字符:(土耳其语字符)

title ~* '(^|[^\wğüşıöçĞÜŞİÖÇ])samsung([^\wğüşıöçĞÜŞİÖÇ]|$)

这里是一个示例案例,您可能需要上面的扩展名。
例如在Latin5编码的数据库中,您具有'İsamsung'的值。 İ是i在土耳其语中的首都。
在这种情况下,title ~* '(\msamsung\M)'不起作用。 此条件返回İsamsung值。因为在Latin5编码中,postgre认为İ不在字母数字范围内,因此该值与:samsung类似。

答案 2 :(得分:0)

以下解决方案已在PostgreSQL 9.6中进行了测试。

您可以使用\y word boundary和带有交替运算符的分组构造来分隔所有可能的选择:

where title ~* '\y(?:Samsung|phone)\y'

或者,不区分大小写:

where title ~* '\y(?:Samsung|phone)\y'

请参见PostgreSQL demo

请注意,当第一个或最后一个字符不是单词char时,\y是不正确的方法。例如。您要搜索#samsungphone#。然后,考虑使用明确的单词边界

where title ~* '(?<!\w)(?:#samsung|phone#)(?!\w)'

在这里,如果当前位置的左侧没有(?<!\w)字符,并且非数字,并且_为负数,则(?!\w)的负向后查找将失败。如果存在非数字并且当前位置的右边没有_字符,则lookahead匹配失败。检验where子句中是否存在匹配项的等效项是:

where title ~* '(\W|^)(?:#samsung|phone#)(\W|$)'

请参见another PostgreSQL demo online

CREATE TABLE mmytable
    (title character varying)
;

INSERT INTO mmytable
    (title)
VALUES
    ('#Samsung Co.'),
    ('They have a phone#'),
    ('Uncle Sam phoned him')
;

select * from mmytable where title ~* '(?<!\w)(?:#samsung|phone#)(?!\w)';

enter image description here

此外,当您只需要在空格字符或字符串的开头/结尾之间进行匹配时,请考虑空格边界

where title ~* '(?<!\S)(?:#samsung|phone#)(?!\S)'
where title ~* '(\s|^)(?:#samsung|phone#)(\s|$)'