下面的示例查询将使用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')
仅返回“Park View”和“Great Lake”。
” - 这是预期的输出结果。 - Travesty3