选择具有字符串最长子字符串的行

时间:2016-03-30 23:16:23

标签: php mysql sql

让我根据下面的例子描述问题。 让我们说有一个字符串" abc12345" (可能是任何!!!)并且有一个表mytable,其列为mycolumn of varchar(100)。

有些行以最后一个字符结尾5 有些行以最后一个字符45结尾 有些行以最后一个字符345结束 没有以最后一个字符2345结尾的行。

在这种情况下,应选择这些行:

SELECT * FROM mytable WHERE mycolumn LIKE "%345"

那是因为" 345"是" abc12345"最长的子串。至少出现一次作为mycolumn列中至少一个字符串的右子字符串。 任何想法如何在一个查询中写入? 谢谢。

3 个答案:

答案 0 :(得分:2)

这是一种蛮力方法:

select t.*
from (select t.*,
             dense_rank() over (order by (case when mycolumn like '%abc12345' then 1
                                               when mycolumn like '%bc12345' then 2
                                               when mycolumn like '%c12345' then 3
                                               when mycolumn like '%12345' then 4
                                               when mycolumn like '%2345' then 5
                                               when mycolumn like '%345' then 6
                                               when mycolumn like '%45' then 7
                                               when mycolumn like '%5' then 8
                                         end)
                              ) as seqnum
      where mycolumn like '%5' -- ensure at least one match  
      from t
     ) t
where seqnum = 1;

然后启发这样的事情:

select t.*
from (select t.*, max(i) over () as maxi
      from t join
           (select str, generate_series(1, length(str)) as i
            from (select 'abc12345' as str) s
           ) s
           on left(t.mycolumn, i) = left(str, i)
     ) t
where i = maxi;

答案 1 :(得分:1)

如果你不能重组表格,我会这样解决问题:

  • 在C中编写聚合UDF LONGEST_SUFFIX_MATCH(col, str)(参见MySQL源代码sql/udf_example.c中的示例,搜索avgcost

  • SELECT @longest_match:=LONGEST_SUFFIX_MATCH(mycol, "abcd12345") FROM mytbl; SELECT * FROM mytbl WHERE mycol LIKE CONCAT('%', SUBSTR("abcd12345", -@longest_match))

如果你可以重组表,我还没有一个完整的解决方案,但首先我要通过反转字符串(通过mycol_rev函数)添加一个特殊列REVERSE()并创建一个键,然后使用该键进行查找。我有时间会发布一个完整的解决方案。

更新

如果您可以添加带有键的反转列:

  • 以`SELECT myrevcol FROM mytbl WHERE myrevcol LIKE CONCAT(SUBSTR(REVERSE(' $ search_string'),$ n),'%')LIMIT的格式使用查询1在1到$ search_string的长度范围内对$ n执行二进制搜索,以查找查询返回行的$ n的最大值
  • SELECT * FROM mytbl WHERE myrevcol LIKE CONCAT(SUBSTR(REVERSE(' $ search_string'),$ found_n),'%')

只要没有太多的行返回,此解决方案应该非常快。我们将总共有O(log(L))查询,其中L是搜索字符串的长度,每个搜索字符串是B树搜索,只读取一行,然后是另一个B树搜索,索引读取只有所需的行。

答案 2 :(得分:1)

有趣的谜题:))

这里最难的问题是找到与后缀模式匹配的目标后缀的长度。

在MySQL中,您可能需要使用生成系列或UDF。其他人已经提出了这些。

在PostgreSQL和其他提供基于regexp的子字符串的系统中,您可以使用以下技巧:

select v,
    reverse(
      substring(
        reverse(v) || '#' || reverse('abcdefg')
        from '^(.*).*#\1.*'
    )) res
from table;

它的作用是:

  • 构造一个组合字符串和后缀的单个字符串。注意,我们反过来。
  • 我们将#放在重要的字符串之间,你需要一个字符串中不存在的字符。
  • 我们使用substring从正则表达式中提取匹配项
    • 它从字符串^
    • 的开头开始
    • 匹配任意数量的字符(.*)
    • 可以包含一些剩余字符.*
    • 现在我们找到#
    • 现在,我们希望在(.*)之后出现与#匹配的相同字符串。所以我们使用\1
    • 并且可以有一些尾部字符.*
    • 我们反转提取的字符串

一旦你有了最长的后缀,找到最大长度,然后找到所有后缀为该长度的字符串是微不足道的。

这是使用PostgreSQL的SQLFiddle