最长匹配子字符串

时间:2013-05-04 00:59:21

标签: postgresql plpgsql

如何在varchar变量中搜索最长匹配?例如,表GOB的条目如下:

magic_word |  prize
===================
         sh|  $0.20
        sha|  $0.40
       shaz|  $0.60
      shaza|  $1.50

我想编写一个plpgsql函数,该函数在其他参数中包含一个字符串作为输入(例如shazam),并返回具有最长匹配子字符串的GOB行上的'prize'列。在显示的示例中,使用magic_word $1.50的行上的shaza

我可以处理的所有函数格式,它只是匹配位。我想不出一个优雅的解决方案。我猜它可能很容易,但我正在挠头。我不知道开头的输入字符串,因为它将从另一个表上的查询结果派生。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

简单解决方案

SELECT magic_word
FROM   gob
WHERE  'shazam' LIKE (magic_word || '%')
ORDER  BY magic_word DESC
LIMIT  1;

这是有效的,因为最长的匹配排序最后 - 所以我排序DESC并选择第一场比赛。

我假设你的例子中你想要从字符串的开头匹配left-anchored。如果你想匹配字符串中的任何地方(这更昂贵,甚至更难用索引备份),请使用:

...
WHERE  'shazam' LIKE ('%' || magic_word || '%')
...

SQL Fiddle.

性能

查询不是sargable。如果您有其他信息(例如可以基于索引的最小长度)来减少要考虑的行数,这可能会有所帮助。它必须是能够让你少于约5%的表才能生效的标准。因此,首字母(自然最小选择)可能有用也可能没用。但是一开始有两三个字母可能会有所帮助。

实际上你可以迭代地优化它。沿着以下方向的东西:
 尝试使用15个字母+
的部分单词索引  如果找不到,请尝试12个字母+
 如果找不到,请尝试9个字母+
 ...

我在dba.SE上的相关答案中概述的一个简单案例:

另一种方法是使用trigram索引。您需要额外的模块pg_trgm。通常,您将使用更长字符串在表格中使用短模式进行搜索。但是三卦也适用于你的反向方法,但有一些限制。很明显,你不能使用三字母组合在一个较长的字符串中间只有两个字符的字符串...测试角情况。
关于更多信息,这里有很多答案。例如:

高级解决方案

整个搜索字符串表这个密切相关的问题下考虑解决方案。使用递归CTE实现:

答案 1 :(得分:1)

怎么样

1

     select max(FOO.matchingValue)
     from
      (
        select magic_word as matchingValue
        from T
        where substr( "abracadabra", 1, length(magic_word)) = magic_word 
      )
      as FOO

2

select prize from
T
  join
  (
  select max(FOO.matchingValue) as MaxValue
     from
      (
         select magic_word as matchingValue
        from T
        where substr( "abracadabra", 1, length(magic_word)) = magic_word 
      )
      as FOO
) as BAR
on BAR.MaxValue = T.magic_word