【问题标题】:Matching strings without space and punctuation in MySQL在 MySQL 中匹配没有空格和标点符号的字符串
【发布时间】:2017-05-23 04:57:09
【问题描述】:

我正在处理一个我认为应该非常直观的查询,但不知何故我在实现它时遇到了一些问题。我想我想要实现的是匹配存储在 MySQL DB 中的字符串,没有空格和标点符号(其他创造性方法非常受欢迎)。同时,我希望查询以变音符号不敏感的方式处理 Unicode 字符(所以像 REGEXP 这样的选项有点不走运)。最后一个条件是我使用的是带有 InnoDB 引擎的 MySQL 5.5,因此不支持全文索引(但如果它有助于解决这个问题,我愿意升级到 5.6/5.7)。

考虑将字符串 Hello-World from John Doe 存储在 DB 中的场景。我想在给定搜索字符串 HelloWorldJohnDoe 时找到它。更一般地说,DB 中的字符串可以包含括号、下存储和任何其他标点符号(不限于 ASCII,但现在可以妥协),而搜索字符串可以是单词的组合,中间有或没有任何分隔符。到目前为止,我得到的最接近的是菊花链 REPLACE 函数以获得已知标点符号列表,如下所示:

SELECT text FROM table WHERE REPLACE(REPLACE(text, '-', ''), ' ', '') LIKE '%JohnDoe%'

我的问题是:

  1. 有没有更好的方法来代替上面的菊花链?
  2. 如果这是唯一的解决方案,那么当我将数百个或更多 REPLACE 函数链接起来时,性能会受到怎样的影响?

提前感谢您的帮助。

【问题讨论】:

    标签: mysql sql regex string innodb


    【解决方案1】:

    我不知道您的搜索必须有多严格,但您可以尝试从中删除所有非字母数字字符,以便最终得到一个匹配的字符串,例如“HelloWorldfromJohnDoe”。

    看看这个答案:How to remove all non-alpha numeric characters from a string?

    您可能需要对其进行一些更改以使其符合您的目的。我将它从 CHAR(32) 更改为 CHAR(255) 以确保我可以获取该列,但您可能需要考虑完全更改该函数以更精确地适合您的数据。

    那么你是这样的:

    SELECT *
    FROM testing
    WHERE alphanum(test) LIKE CONCAT('%', alphanum('John Doe'), '%')
    

    这应该会给你一个打击。

    【讨论】:

    • 感谢您的指点。事实上,我已经研究过那个线程,不幸的是我也必须处理 CJK 字符。我将尝试修改函数以摆脱 [[:punct:]] 而不是保留 [[:alnum:]],看看是否工作。
    【解决方案2】:

    方法一

    我会在架构上的另一列包含名称的“散列”版本,例如,假设您有用户:

    John Doe The Great
    

    这个名字散列到

    johndoethegreat
    

    哈希函数的编码方式使得以下所有字符串:

    John_Doe_THE_great
    John Doe The GREAT
    John.Doe.The.Great
    johnDOE___theGreat
    john   Doe   the     great
    ___john____DOE____THE____great
    

    散列到相同的值

    johndoethegreat
    

    编写这样的函数很简单。这样您就可以获取用户输入,对其进行哈希处理,然后将其与数据库中的哈希列进行比较

    类似的名字:

    Jon Doe
    John Doo
    

    当然不会被发现

    方法二

    使用 MySQL 内置的FULLTEXT 搜索功能,按分数对结果进行排序并选择第一个非零条目

    http://blog.oneiroi.co.uk/mysql/php/mysql-full-text-search-with-percentage-scoring/

    【讨论】:

      【解决方案3】:

      我完全错过了你问题的重点。你似乎有字符串:

      • Hello-World from John Doe

      如果要在搜索字符串为JohnDoeJohn Doe时找到这个,则只需替换空格即可:

      where replace(text, ' ') like concat('%', 'JohnDoe', '%')
      

      如果你想要一个同时包含“John”和“Doe”的字符串,那么:

      where replace(text, ' ') like concat('%', 'John%Doe', '%')
      

      我不明白为什么需要 100 个嵌套的 replace()s。

      【讨论】:

      • 问题是,分隔符可以是任何标点符号,不限于连字符或空格。一些记录可能有底层存储,一些记录可能有括号,例如 John_DoeJohn (Doe)。我希望所有这些记录都与搜索字符串 JohnDoe 匹配,希望这对您有意义。
      • @CLDev 。 . .这就是like '%John%Doe%' 的处理方式。
      • 同意,但是如果输入的用户不知道上下文并输入 JohnDoe 怎么办? (我无法控制用户输入,即搜索字符串。)然后我必须对短语进行标记?
      • @CLDev 。 . .在某些时候,您必须相信用户知道他们在寻找什么。
      • 我是否信任用户不是问题,但我正在将其视为一个实际问题......有些人添加连字符(或其他标点符号)的地名或词组但有些没有。例如,我看到有些人键入 re-engineering,而有些人键入 reengineering。所以我现在得到的是后者,一些用户希望得到带有前者的文章作为回报。
      猜你喜欢
      • 1970-01-01
      • 2021-11-17
      • 1970-01-01
      • 2011-08-16
      • 2013-07-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 2023-02-24
      相关资源
      最近更新 更多