下面的示例查询将使用 MySQL 为您提供指定的结果集,但它并没有真正进行“模糊匹配”,至少,这不是我描述算法的方式。(这实现了您描述的算法 - 按值排序,然后检查每个值以查看前导部分是否“匹配”先前检索到的值。)
这会找到邻域值的前导部分与先前检索到的行的值的“精确匹配” ,实际上并没有任何关于匹配的“模糊性”。
当查询遇到一个“不匹配”的值时,它会将这个值标记为“不匹配”。对于检索到的下一个值,它检查该值是否以先前“不匹配”的值开头;如果字符串的前导部分完全匹配,则丢弃该值。否则,该值被标记为“不匹配”值,并被保留。
这种方法使用内联视图(或 MySQL 所指的“派生表”)。最里面的内联视图(别名为 s)为我们提供了邻域不同值的排序列表。“技巧”(如果你想这么称呼它)在下一个内联视图(别名为“t”)中,我们使用 MySQL 用户变量来引用以前检索到的值。
为了避免“特殊字符”的任何问题,我们对前导字符进行相等比较。
这是整个查询:
SELECT t.neighborhood
FROM (
SELECT IF(IFNULL(LEFT(s.neighborhood,CHAR_LENGTH(@match)) <> @match,1),@match := s.neighborhood,NULL) AS neighborhood
FROM (SELECT RTRIM(neighborhood) AS neighborhood
FROM mytable
JOIN (SELECT @match := NULL) r
GROUP BY neighborhood
ORDER BY neighborhood
) s
) t
WHERE t.neighborhood IS NOT NULL
这一切都非常简单,除了 @match 变量的初始化,以及执行当前值与前一个值比较的表达式。
如果我们不关心值中特殊字符引入的极端情况,我们可以使用更简单的 LIKE 或 REGEXP 进行比较:
s.neighborhood NOT LIKE CONCAT(@match,'%')
s.neighborhood NOT REGEXP CONCAT('^',@match)
LIKE 运算符受下划线和百分号字符的约束,REGEXP 受正则表达式中使用的特殊字符的约束。为了避免这些问题,上面的查询使用了一个看起来有点笨拙的比较:
LEFT(s.neighborhood,CHAR_LENGTH(@match)) <> @match
这样做是取上一个值(例如@match := 'Park View')并将其与下一个值的前导部分(直到'Park View' 的长度)进行比较,确定它是否匹配。
这种查询方法的一个好处是返回的值保证在后续查询中的谓词中“匹配”。假设您正在使用此查询来获取社区列表,并且用户选择了一个。这将返回一组将“匹配”到每一行的值。
后续查询可以使用简单谓词(WHERE 子句)中的任何返回值来返回匹配的行。例如,如果用户选择了值“Great Lake”:
SELECT t.*
FROM mytable t
WHERE LEFT(t.neighborhood,CHAR_LENGTH('Great Lake') = 'Great Lake'
在我们使用 LIKE 或 REGEXP 谓词进行匹配的情况下,我们希望在后续查询的谓词中使用相应的匹配:
SELECT t.*
FROM mytable t
WHERE t.neighborhood LIKE CONCAT('Great Lake','%')
SELECT t.*
FROM mytable t
WHERE t.neighborhood REGEXP CONCAT('^','Great Lake')