【问题标题】:Find rows in Postgres where string is in any random order在 Postgres 中查找字符串以任意随机顺序排列的行
【发布时间】:2016-01-04 13:27:30
【问题描述】:

我目前正在研究一个 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. Lo​​rem ipsum dolor
    2. dolor Lorem ipsum
    3. ipsum dolor Lorem
    4. Lo​​rem dolor ipsum
    5. ...

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

【问题讨论】:

标签: sql postgresql


【解决方案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】:
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

【讨论】:

  • 感谢这就像魅力一样。目前唯一的问题是性能,但现在它可以工作。
【解决方案3】:

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

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 在 cmets 中所说,他只需要只包含三个搜索词的注册表,我会采用这种方法:

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

【讨论】:

  • 但如果我错了,请纠正我。这也将返回,例如Lorem ipsum dolor sit amet。结果应仅包含任意随机顺序的 3 个单词。我的问题不够清楚,会更新它。
  • @lvdp 是的,你是对的,是的,这并不清楚!我会更新的。
  • 主要缺点是必须动态构建。最后一个版本匹配'loremyy dolorxx ipsumzz'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-18
  • 1970-01-01
  • 1970-01-01
  • 2012-01-04
  • 1970-01-01
相关资源
最近更新 更多