我目前正在开发一个Postgres SQL查询,该查询将检查以任意随机顺序匹配空格分隔字符串(3个单词)的行。
例如,我想查找一个匹配"lorem ipsum dolor"的行,它应该返回行id 0。
+----+-------------------+
| id | sentence |
| 0 | lorem dolor ipsum |
| 1 | lorem ipsum |
| 2 | ipsum dolor |
| 3 | ipsum dolor |
+----+-------------------+
因此它必须满足以下条件:
- 在这种情况下是相同的3个单词
- 按任意随机顺序:
- Lorem ipsum dolor
- dolor Lorem ipsum
- ipsum dolor Lorem
- Lorem dolor ipsum
如果我是正确的,这将导致3*3*3=27种可能的格式。但这可以想象,当使用更多的单词时,这是相当密集的。我如何在不破坏服务器的情况下实现这一点,或者寻找正确的方向。
Clodoaldo Neto描述的方法非常适合对单词进行排序。如果性能对您来说至关重要,您甚至可以为此创建索引以提高查找速度。首先创建自定义函数sortwords
:
CREATE OR REPLACE FUNCTION sortwords (words text) RETURNS text AS
$$ SELECT string_agg(lower(s), ' ' order by s)
FROM regexp_split_to_table($1, 's+') s(s) $$
LANGUAGE sql IMMUTABLE;
关键字IMMUTABLE
指定,函数结果仅取决于其参数,因此该函数适用于创建索引。
然后,创建索引:
CREATE INDEX mytable_sortwords ON mytable (sortwords(sentence));
并执行选择,如:
SELECT * FROM mytable WHERE sortwords(sentence) = sortwords('some words');
这样做的优点是,单词的排序(可能非常昂贵)每行只执行一次(无论是在创建索引时还是在行插入时)。
with t(s) as (values
('lorem dolor ipsum'),
('lorem ipsum'),
('ipsum dolor'),
('ipsum dolor')
)
select *
from t
where
(
select string_agg(lower(s), ' ' order by s)
from regexp_split_to_table(s, 's+') s(s)
)
=
(
select string_agg(lower(s), ' ' order by s)
from regexp_split_to_table('lorem ipsum dolor', 's+') s(s)
)
;
s
-------------------
lorem dolor ipsum
http://www.postgresql.org/docs/current/static/functions-aggregate.htmlhttp://www.postgresql.org/docs/current/static/functions-string.html#FUNCTIONS-字符串-其他
实现这一点的一种方法是打破搜索文本,然后添加如下过滤器:
select * from test
where sentence like '%lorem%'
and sentence like '%ipsum%'
and sentence like '%dolor%';
这将得到包含这三个单词的所有句子,按任何顺序排列。请在此处查看它的工作情况:http://sqlfiddle.com/#!15/28ac1/3
编辑
为了在任何情况下获得结果,您必须向字段sentence
添加lower
函数,如下所示:
select * from test
where lower(sentence) like '%lorem%'
and lower(sentence) like '%ipsum%'
and lower(sentence) like '%dolor%';
请在此处查看:http://sqlfiddle.com/#!15/6dfb9/1
编辑2
正如OP在评论中所说,他只需要只包含三个搜索词的注册表,我会采用这种方法:
select * from test
where position('lorem' in lower(sentence))>0
and position('ipsum' in lower(sentence))>0
and position('dolor' in lower(sentence))>0
and array_length(regexp_split_to_array(sentence, E'\s+')::text[],1) =
array_length(regexp_split_to_array('lorem ipsum dolor', E'\s+')::text[],1)
请在此处查看它的工作情况:http://sqlfiddle.com/#!15/a5404/5