【问题标题】:MySQL - search for patternsMySQL - 搜索模式
【发布时间】:2016-08-12 10:02:55
【问题描述】:

我试图弄清楚是否有人有一种优雅的方式来查找存储在 varchar 字段中的数据模式,其中值未知 - 这意味着我不能使用 LIKE。例如,假设一个名为 test 的表如下所示:

id, str

数据看起来像这样:

1, YUUUY
2, DDDMM
3, MMMMT
4, XMXMX

并且我想做一个选择,它将返回 str 的值具有与模式 ABABA 匹配的模式的任何内容。 ABABA 这里显示的是一种模式,而不是文字字母。因此,唯一匹配此模式的将是 id = 4。是否有一个正则表达式可以用来进行这样的模式匹配?为了确保我清楚这些模式:

The pattern for id=1 is ABBBA.  
The pattern for id=2 is AAABB.  
The pattern for id=3 is AAAAB.

运行查询时,我只知道要搜索的模式。

或者,如果它更容易,我可以将表格设置为:

id,c1,c2,c3,c4,c5

数据看起来像这样:

1,Y,U,U,U,Y
2,D,D,D,M,M
3,M,M,M,M,T
4,X,M,X,M,X

不确定这是否会变得更容易,但我认为如果数据是这样设置的,那么正则表达式就不合适了。

【问题讨论】:

  • 有些事情不应该在 SQL 中完成。

标签: mysql regex


【解决方案1】:

MySQL 中不支持正则表达式来进行这种模式匹配,不。

SQL 并不是专门为字符串模式匹配(或不同列中的值模式)而设计的。

但是......我们可以想出一些可行的方法,即使它不是正则表达式并且它不优雅。

假设我们没有自定义构建的用户定义函数,并且我们想使用原生 MySQL 函数和表达式...

假设我们正在寻找的模式保证只包含两个不同的字符......

假设我们正在查看五个字符位置...

假设我们要匹配的模式字符串总是以字母'A'开头,而模式中的“其他”字母也将是'B'

这样做不会太难看:

SELECT t.id
     , t.str
  FROM myable t         
WHERE CONCAT('A'
        ,IF(MID(t.str,2,1)=MID(t.str,1,1),'A','B')
        ,IF(MID(t.str,3,1)=MID(t.str,1,1),'A','B')
        ,IF(MID(t.str,4,1)=MID(t.str,1,1),'A','B')
        ,IF(MID(t.str,5,1)=MID(t.str,1,1),'A','B')
      ) = 'ABBBA'

字符串中的第一个字符会自动转换为“A”。

第二个字符,如果匹配第一个字符,那么它也是一个'A',否则它是一个'B'。

我们对第三、第四和第五个字符做同样的事情。

将 'A' 和 'B' 字符连接成一个字符串,我们现在可以对以 'A' 开头的由 'A' 和 'B' 组成的模式字符串执行相等比较。

但是,如果所述假设不正确,这将分崩离析。如果 str 的长度小于五个字符,如果它包含两个以上不同的字符(我们将看到第一个字符匹配...这将看到 str=XYYZX 匹配模式 ABBBA。(第一个字符自动匹配到 A , 第五个字符匹配第一个字符,所以它是 A,而所有其他字符都不匹配,所以它们是 'B',即使它们不一样。

等等。

我们可以添加一些额外的检查。

例如,要保证 str 的长度正好是五个字符...

AND CHAR_LENGTH(t.str)=5

请注意,MySQL 中的默认排序规则不区分大小写。这意味着 MmmmM 的 str 值将转换为“AAAAA”,而不是“ABBBA”。 MmmKk 的 str 值将匹配 'AAABB'。

【讨论】:

  • 感谢斯宾塞提供的信息。我还应该说明该列可能没有 5 个字符。可能是 1 到 8,我还没有决定是否允许在模式 ex 中使用 C。 ABCBA。这使得这种类型的解决方案更加复杂。我可能会在 PHP 而不是 MySQL 中处理它。再次感谢您的回复,我喜欢您使用解决方案的地方。我可能会使用类似的东西来完成任务。
  • @Travis:我给出的示例看起来很简单,我认为它适用于问题中的所有示例。它看起来很简单,因为它只处理一小部分特殊情况。扩展和推广这种方法会很乏味,表达式会变得过于复杂。
【解决方案2】:

不幸的是,MySQL 似乎不支持正则表达式组。我希望你能做这样的事情来匹配 ABBBA,例如:

([A-Z])([A-Z])\2\2\1

此处示例:http://regexr.com/3d8gu

好像有一个 MySQL 插件可能支持它:

https://github.com/mysqludf/lib_mysqludf_preg

这是一个真正的 hacky 方法。

ABBBA(或 YUUUY 等):

SELECT id, name FROM table WHERE    
  substring(name,1,1) = substring(name,5,1) AND      
  substring(name,2,1) = substring(name,3,1) AND
  substring(name,3,1) = substring(name,4,1);

AAABB(或 DDDMM 等):

SELECT id, name FROM table WHERE    
  substring(name,1,1) = substring(name,2,1) AND      
  substring(name,2,1) = substring(name,3,1) AND
  substring(name,4,1) = substring(name,5,1);

AAAAB(或 MMMMT 等):

SELECT id, name FROM table WHERE    
  substring(name,1,1) = substring(name,2,1) AND      
  substring(name,2,1) = substring(name,3,1) AND
  substring(name,3,1) = substring(name,4,1) AND
  substring(name,4,1) != substring(name,5,1);

你明白了……

如果您将数据分成不同的列,情况会很相似。您只需比较列,而不是比较子字符串。

【讨论】:

  • 感谢马克的信息。我一直在寻找像正则表达式组示例这样的东西,但我现在还没有准备好安装插件。我有几个 hacky 方法来完成它,看起来我必须这样做。我还应该说明该列可能没有 5 个字符。可能是 1 到 8,我还没有决定是否允许在模式 ex 中使用 C。 ABCBA。这使得 hacky 解决方案更加复杂。我可能会在 PHP 而不是 MySQL 中处理它。再次感谢您的回复。
猜你喜欢
  • 2012-04-20
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 2017-01-01
  • 1970-01-01
  • 2014-05-27
  • 1970-01-01
  • 2013-06-22
相关资源
最近更新 更多