选择字符串中子串最长的行
Posted
技术标签:
【中文标题】选择字符串中子串最长的行【英文标题】:select rows with longest substring of the string 【发布时间】:2016-03-30 23:16:22 【问题描述】:让我根据下面的例子来描述这个问题。 假设有一个字符串“abc12345”(可以是任何!!!),并且有一个表 mytable,其列 mycolumn 为 varchar(100)。
有些行以最后一个字符 5 结尾。 有些行以最后一个字符 45 结尾。 有一些行以最后一个字符 345 结尾 没有以最后一个字符 2345 结尾的行。
在这种情况下,应该选择这些行:
SELECT * FROM mytable WHERE mycolumn LIKE "%345"
这是因为“345”是“abc12345”的最长右子字符串,它作为 mycolumn 列中至少一个字符串的右子字符串至少出现一次。 任何想法如何在一个查询中编写它? 谢谢。
【问题讨论】:
【参考方案1】:这是一种蛮力方法:
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;
【讨论】:
【参考方案2】:有趣的谜题:)
这里最困难的问题是找到与您的后缀模式匹配的目标后缀的长度。
在 mysql 中,您可能需要使用生成系列或 UDF。其他人已经提出了这些建议。
在 PostgreSQL 和其他提供基于正则表达式的子字符串的系统中,您可以使用以下技巧:
select v,
reverse(
substring(
reverse(v) || '#' || reverse('abcdefg')
from '^(.*).*#\1.*'
)) res
from table;
它的作用是:
构造一个结合你的字符串和后缀的字符串。注意,我们颠倒它们。 我们将#
放在重要的字符串之间,您需要一个字符串中不存在的字符。
我们使用substring
从正则表达式中提取匹配,这样
从字符串^
的开头开始
匹配任意数量的字符(.*)
可以有一些剩余字符.*
现在我们找到#
现在,我们希望与(.*)
匹配的相同字符串出现在#
之后。所以我们使用\1
并且可以有一些尾字符.*
我们反转提取的字符串
一旦你有最长的后缀,找到最大长度,然后找到所有具有该长度后缀的字符串是微不足道的。
这是一个使用 PostgreSQL 的SQLFiddle:
【讨论】:
【参考方案3】:如果您无法重组表格,我会以这种方式解决问题:
在 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 对 $n 执行二进制搜索的查询从 1 到 $search_string 长度的范围,以找到查询返回行的 $n 的最大值 SELECT * FROM mytbl WHERE myrevcol LIKE CONCAT(SUBSTR(REVERSE('$search_string'), $found_n),'%')只要返回的行数不多,此解决方案应该会非常快。我们将有总共 O(log(L)) 个查询,其中 L 是搜索字符串的长度,每个查询都是 B-tree 搜索,只读取一行,然后是另一个 B-tree 搜索,索引读取只包含所需的行。
【讨论】:
以上是关于选择字符串中子串最长的行的主要内容,如果未能解决你的问题,请参考以下文章