Oracle:多列“最接近匹配”自动完成策略

Posted

技术标签:

【中文标题】Oracle:多列“最接近匹配”自动完成策略【英文标题】:Oracle: Multi-Column "Closest Match" Autocomplete Strategy 【发布时间】:2012-05-17 14:23:00 【问题描述】:

我正在实现一个自动完成功能,它允许用户输入部分文本,然后与表中的 4 个不同列进行匹配。这是一个基本示例:

+------------+-----------+--------+-----------------+
| first_name | last_name | login  | email           |
+------------+-----------+--------+-----------------+
| John       | Smith     | jsmith | jsmith@foo.bar  |
| Johnny     | Ringo     | ringo  | ringer@hmm.okay |
| Bob        | Jones     | bjones | j1234@xyz.abc   |
| Jane       | Doe       | doej   | doedoe@blah.umm |
+------------+-----------+--------+-----------------+

当用户输入“jo”时,我想匹配该表中的记录,其中这四列中至少有一个与模式“jo%”匹配。对于此示例,由于它们的 first_name 列值,只有前两个会匹配。如果搜索是“js”,那么由于其 loginemail 列值,只有第一条记录会匹配。等等。我还想返回按相似度排序的结果,其中第一个结果是“最接近的匹配”,依此类推(标准自动完成行为)。

我目前一直在尝试使用UTL_MATCH 和产生以下查询的代码来解决这个问题:

SELECT first_name,
  last_name,
  login,
  email,
      (  utl_match.jaro_winkler_similarity(first_name, 'js')
       + utl_match.jaro_winkler_similarity(last_name, 'js')
       + utl_match.jaro_winkler_similarity(login, 'js')
       + utl_match.jaro_winkler_similarity(email, 'js')) similarity
FROM users
WHERE LOWER(first_name) LIKE LOWER('js%')
OR LOWER(last_name) LIKE LOWER('js%')
OR LOWER(login) LIKE LOWER('js%')
OR LOWER(email) LIKE LOWER('js%')
ORDER BY similarity DESC

结果并不像我希望的那样准确,而且我已经看到野外的自动完成功能按照我希望的方式工作,但不知道它们是如何实现的后端。

谁能指出我正确的方向?

【问题讨论】:

你能举一个例子说明结果不是你想要的,你希望它们是什么,为什么?我的第一个猜测是,您希望按最大相似性而不是 4 个组件的相似性总和对结果进行排序,和/或您希望对字段进行加权以匹配用户最有可能搜索的内容.但是如果没有更多细节就很难知道。 @JustinCave:不幸的是,由于保密协议,我不能。但是你提出了一些我以前没有想到的有趣的想法。我使用总数的原因是用户可以搜索多个术语(例如“john sm”),并且我将每个以空格分隔的单词视为一个不同的搜索词,对每个词应用所有相同的逻辑。不过,它正在迅速变得笨拙。 显然,我不是要求您发布真实数据、真实表结构等。只是您发布的示例数据或多行示例数据的示例,其中排序不是' t 你想要什么。当您搜索您提供的示例“jo”或“js”时,您发布的示例数据似乎工作得相当好,因此很难看出问题是如何回答的。如果您可以说“当我搜索 'jo' 时,我宁愿将 'John Smith' 排序在 'Bob Jones' 之上,因为......”我们可能会提供帮助。否则,我认为这个问题是无法回答的。 为什么不围绕匹配制定一些基本规则(例如,4 个字段中的任何一个以输入开头)。所以用户输入“j”,将返回所有行(John,Johnny,Jones,Jane),但对于“jo”,只会得到 3 行(John,Johnny,Jones)。将您的 SQL 简化为仅使用 like 'jo%' 语法,并且在 imo 中更加一致和易于理解。 @tbone:我已经在上面的查询中这样做了。 【参考方案1】:

搜索总是很有趣。你已经开始了一个非常好的基本方法。我通常做的是创建一个通过触发器加载的辅助表。触发器从您的用户表中加载主键,第二列是“搜索”列。在您的示例中,每一行将产生 4 行。确保以大写形式存储结果,以便您可以索引列,Oracle 将使用该索引,因为您坚持使用类似的语法。它需要一个额外的表,但是可以通过触发器来维护该表。

    create table searcher
    (
    user_id  number, --FK back to users
    search_column  varchar2(50) -- or whatever size column is appropriate
    );

    select u.first_name, u.last_name, u.login. u.email,
utl_match.jaro_winkler_similarity(search_column, 'js')
    from users u
    join
    searcher s
    on (u.user_id = s.user_id)
    where s.search_column like upper('js%')
    order by 5;

【讨论】:

我认为您在这里的建议与@JustinCave 关于使用所有列的最大相似度而不是总数的建议基本相同。不同之处在于在查询中取最大值不需要单独的表。这种方法可能对性能有一点好处,但我不确定。 主要区别在于我的解决方案将索引您的查询并提高性能。如果您的表中只有 100 行,则可能无关紧要。如果你有 100,000,那么这可能是个问题。

以上是关于Oracle:多列“最接近匹配”自动完成策略的主要内容,如果未能解决你的问题,请参考以下文章

脚本参数匹配

MS Access 2010 多列组合框自动完成

[自动运维]oracle自动备份

TOAD for Oracle - 自动完成

php + mysql - 性能 - 即时自动完成响应

Oracle SQL 多列与单个范围/图例对齐