用于比较列中每个单词的开头的索引

时间:2017-01-31 11:28:44

标签: sql postgresql psycopg2

所以我有一张桌子

id | name            | gender
---+-----------------+-------
0  | Markus Meskanen | M
1  | Jack Jackson    | M
2  | Jane Jackson    | F

我已经创建了一个索引

CREATE INDEX people_name_idx ON people (LOWER(name));

然后我用

查询
SELECT * FROM people WHERE name LIKE LOWER('Jack%');

%(name)s是用户的输入。但是,它现在只匹配整个列的开头,但我希望它匹配任何单词的开头。我不想使用'%Jack%',因为它也会导致单词中间的结果无效。

有没有办法创建索引,以便每个单词都有一个单独的行?

修改:如果名称与'Michael Jackson's First Son Bob'长相似,则应与任何字词的开头匹配,即MichMichael匹配并且Fir会与First匹配,但ackson与任何内容都不匹配,因为它不是从头开始。

编辑2:我们有300万行,因此性能问题,因此我主要关注的是索引。

4 个答案:

答案 0 :(得分:2)

Postgres有两种索引类型可以帮助进行全文搜索:GIN和GIST索引(我认为GIN是更常用的索引)。

documentation中的索引简要概述。每个索引类都有更广泛的文档,以及关于该主题的大量博客(here是一个而here是另一个)。

这些可以加快您尝试进行的搜索。

答案 1 :(得分:1)

pg_trgm module完全符合您的要求。

您需要创建:

CREATE INDEX people_name_idx ON people USING GIST (name gist_trgm_ops);

或者:

CREATE INDEX people_name_idx ON people USING GIN (name gin_trgm_ops);

See the difference here.

之后,这些查询可以使用上述索引之一:

SELECT * FROM people WHERE name ILIKE '%Jack%';
SELECT * FROM people WHERE name ~* '\mJack';

作为@GordonLinoff answeredfull text search也能够通过前缀匹配进行搜索。但是FTS并不是为了有效地做到这一点,它最适合匹配词位。虽然如果你想获得最佳表现,我建议你试一试。&衡量每个。在FTS中,您的查询看起来像这样:

SELECT * FROM people WHERE to_tsvector('english', name) @@ to_tsquery('english', 'Jack:*');

注意:但是,如果您的查询过滤器(Jack)来自用户输入,则上述两个查询都需要一些保护(即您需要的ILIKE个查询转义%_字符,在regexp中你需要逃避更多,而在FTS中,你需要用一些解析器解析查询并生成一个有效的FTS' tsquery查询,因为如果to_tsquery()的参数无效,plainto_tsquery()会给您一个错误。而在name ~* '\mJack'中,您不能使用匹配查询的前缀。)

注释2 æ的正则表达式变体最适合英文名称。如果你想使用整个unicode范围(即你想使用像SELECT * FROM people WHERE name ~* '(^|\s|,)Jack'; 这样的字符),你需要一个稍微不同的模式。类似的东西:

O'Brian

这适用于大多数名称,此外,它也可以像真正的前缀匹配一些旧名称一样工作,例如class Test { ... public static getGetters(): string[] { return Object.keys(this.prototype).filter(name => { return typeof Object.getOwnPropertyDescriptor(this.prototype, name)["get"] === "function" }); } public static getSetters(): string[] { return Object.keys(this.prototype).filter(name => { return typeof Object.getOwnPropertyDescriptor(this.prototype, name)["set"] === "function" }); } } Test.getGetters(); // ["RowsCount", "RowsCount2"] Test.getSetters(); // ["RowsCount", "RowsCount2"]

答案 2 :(得分:0)

您可以使用Regex expressions查找名称中的文字:

create table ci(id int, name text);

insert into ci values
(1, 'John McEnroe Blackbird Petrus'),
(2, 'Michael Jackson and Blade');

select id, name
from ci
where name ~ 'Pe+'
;

返回:

    1   John McEnroe Blackbird Petrus

或者可以使用类似的where substring(name, <regex exp>) is not null

在此处查看:http://rextester.com/LHA16094

答案 3 :(得分:-1)

如果您知道单词是空格分隔的,则可以

SELECT * FROM people WHERE name LIKE LOWER('Jack%') or  name LIKE LOWER(' Jack%')  ;

要获得更多控制权,您可以将RegEx与MySQl一起使用

请参阅https://dev.mysql.com/doc/refman/5.7/en/regexp.html