在Postgres中查找字符串以任意随机顺序排列的行

时间:2016-01-04 13:27:30

标签: sql postgresql

我目前正在研究Postgres SQL查询,该查询将检查以任意随机顺序匹配空格分隔字符串(3个字)的行。

例如,我想查找匹配“lorem ipsum dolor”的行,它应该返回行ID 0.

+----+-------------------+
| id | sentence          |
| 0  | lorem dolor ipsum |
| 1  | lorem ipsum       |
| 2  | ipsum dolor       |
| 3  | ipsum dolor       |
+----+-------------------+

因此必须满足以下条件:

  • 在这种情况下相同的3个字
  • 以任意随机顺序:
    1. Lorem ipsum dolor
    2. dolor Lorem ipsum
    3. ipsum dolor Lorem
    4. Lorem dolor ipsum
    5. ...

如果我是正确的,这应该导致3 * 3 * 3 = 27种可能的格式。但是,这可以想象,当使用更多单词时,这相当密集。如何在不锤击服务器的情况下实现这一目标,或者找到正确的方向。

3 个答案:

答案 0 :(得分:1)

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.html http://www.postgresql.org/docs/current/static/functions-string.html#FUNCTIONS-STRING-OTHER

答案 1 :(得分:1)

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');

这样做的好处是,单词的排序(可以非常省时)每行只执行一次(无论是创建索引还是插入行)。

答案 2 :(得分:0)

实现此目的的一种方法是中断搜索文本,然后将其添加为以下过滤器:

select * from test 
 where sentence like '%lorem%'
   and sentence like '%ipsum%'
   and sentence like '%dolor%';

这将以任何顺序获得包含这三个单词的所有句子。 看到它在这里工作:http://sqlfiddle.com/#!15/28ac1/3

修改

为了在任何情况下获得结果,您必须将lower函数添加到字段sentence,如下所示:

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