sql - 姓名查询数据库建议

标签 sql oracle full-text-search soundex oracle-text

我有一个 Oracle 数据库,与许多数据库一样,它有一个包含传记信息的表。对此,我想以“自然”的方式按名称搜索。

该表有forenamesurname领域,目前,我正在使用这样的东西:

select id, forename, surname
from   mytable
where  upper(forename) like '%JOHN%'
and    upper(surname) like '%SMITH%';

这是有效的,但它可能会非常慢,因为该表上的索引显然无法解释前面的通配符。此外,用户通常会根据他们在电话中告诉他们的内容来搜索人员——包括大量的非英语姓名——因此也可以进行一些语音分析。

因此,我一直在试验 Oracle Text:
create index forenameFTX on mytable(forename) indextype is ctxsys.context;
create index surnameFTX on mytable(surname) indextype is ctxsys.context;

select   score(1)+score(2) relevance,
         id,
         forename,
         surname
from     mytable
where    contains(forename,'!%john%',1) > 0
and      contains(surname,'!%smith%',2) > 0
order by relevance desc;

这具有使用 Soundex 算法以及全文索引的优点,因此应该更有效一些。 (虽然,我的轶事结果表明它非常慢!)我对此唯一的担忧是:
  • 首先,需要以某种有意义的方式刷新文本索引。使用 on commit会太慢并且可能会干扰我无法控制的前端软件与数据库的交互方式;所以需要一些思考...
  • Oracle 返回的结果并不是很自然地排序;我不太确定这个 score功能。例如,我的开发数据显示“Jonathan Peter Jason Smith”在顶部——很好——但“Jane Margaret Simpson”与“John Terrance Smith”处于同一级别

  • 我认为删除前面的通配符可能会在不降低结果的情况下提高性能,因为在现实生活中,您永远不会在名称中间搜索块。但是,否则,我愿意接受想法......这个场景一定是令人作呕的!任何人都可以对我现在正在做/正在考虑的事情提出更好的方法吗?

    谢谢 :)

    最佳答案

    按照评论中的建议,我提出了一个非常有效的解决方案。特别是@X-Zero 关于创建 Soundexes 表的建议:就我而言,我可以创建新表,但不允许更改现有架构!

    所以,我的流程如下:

  • 创建一个包含列的新表: IDtokensoundposition ;主键( IDsoundposition )和附加索引( IDsound )。
  • 遍历履历表中的每个人:
  • 连接他们的名字和姓氏。
  • 将代码页更改为 us7ascii ,因此重音字符被标准化。这是因为 Soundex 算法不适用于重音字符。
  • 将所有非字母字符转换为空格,并将其视为标记之间的边界。
  • 对这个字符串进行标记化,并将标记(小写)、标记的 Soundex 和标记在原始字符串中的位置插入表中;将此与 ID 相关联。

  • 像这样:
    declare
      nameString varchar2(82);
      token varchar2(40);
      posn integer;
      cursor myNames is
        select id,
               forename||' '||surname person_name
        from   mypeople;
    begin
      for person in myNames
      loop
        nameString := trim(
                        utl_i18n.escape_reference(
                          regexp_replace(
                            regexp_replace(person.person_name,'[^[:alpha:]]',' '),
                            '\s+',' '),
                          'us7ascii')
                        )||' ';
        posn := 1;
        while nameString is not null
        loop
          token := substr(nameString,1,instr(nameString,' ') - 1);
          insert into personsearch values (person.id,lower(token),soundex(token),posn);
          nameString := substr(nameString,instr(nameString,' ') + 1);
          posn := posn + 1;
        end loop;
      end loop;
    end;
    /
    

    因此,例如,“Siân O'Conner”被标记为“sian”(位置 1)、“o”(位置 2)和“conner”(位置 3),这三个条目及其 Soundex 被插入到 personsearch连同他们的身份证。
  • 为了搜索,我们执行相同的过程:标记搜索条件,然后返回 Soundexes 和相对位置匹配的结果。我们依次按位置和 Levenshtein 距离(ld)与原始搜索的每个标记进行排序。

  • 例如,此查询将搜索两个标记(即预先标记的搜索字符串):
    with     searchcriteria as (
             select 'john'  token1,
                    'smith' token2
             from   dual)
    select   alpha.id,
             mypeople.forename||' '||mypeople.surname
    from     peoplesearch alpha
    join     mypeople
    on       mypeople.student_id = alpha.student_id
    join     peoplesearch beta
    on       beta.student_id = alpha.student_id
    and      beta.position   > alpha.position
    join     searchcriteria
    on       1 = 1
    where    alpha.sound = soundex(searchcriteria.token1)
    and      beta.sound  = soundex(searchcriteria.token2)
    order by alpha.position,
             ld(alpha.token,searchcriteria.token1),
             beta.position,
             ld(beta.token,searchcriteria.token2),
             alpha.student_id;
    

    要搜索任意数量的标记,我们需要使用动态 SQL:连接搜索表的次数与标记的数量一样多,其中连接表中的 position 字段必须大于先前连接表的 position。我计划编写一个函数来执行此操作——以及搜索字符串标记化——它将返回一个 ID 表。但是,我只是在这里发布这个,所以你明白了:)

    正如我所说,这非常有效:它很快就会返回良好的结果。甚至搜索“John Smith”,一旦被服务器缓存,运行时间不到0.2s;返回超过 200 行...我对它非常满意,并希望将其投入生产。唯一的问题是:
  • token 的预计算需要一段时间,但它是一个一次性的过程,所以问题不大。然而,一个相关的问题是,每当在 mypeople 上执行相应的操作时,都需要在 mypeople 表上放置触发器以将标记插入/更新/删除到搜索表中。这可能会减慢系统速度;但由于这应该只发生在一年中的几个时期,也许更好的解决方案是按计划重建搜索表。
  • 没有进行词干提取,因此 Soundex 算法仅匹配完整标记。例如,搜索“chris”不会返回任何“christopher”。一个可能的解决方案是只存储 token 词干的 Soundex,但计算词干并不是一个简单的问题!这将是 future 的升级,可能会使用 TeX 使用的连字引擎...

  • 无论如何,希望有帮助:) 欢迎评论!

    EDIT 我的完整解决方案(编写和实现)现在是 here ,使用 Metaphone 和 Damerau-Levenshtein 距离。

    关于sql - 姓名查询数据库建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7612822/

    相关文章:

    mysql - 仅当值不存在时返回行

    c# - 为什么参数比 where 子句中的文字值慢?

    sql - 匹配所有数字直到第一个空格

    java - ORA-02289 : sequence does not exist, 但是序列已存在于数据库中

    postgresql - Postgres 的全文搜索什么时候支持词组匹配和邻近匹配?

    mysql - 基于内部选择的计数

    Mysql,如果所有列都不存在则插入

    sql - ORA-02437 : cannot validate <name> - primary key violated

    sql - 为什么我要费心使用全文搜索?

    perl - 如何从 Perl 对 PDF 文件进行全文搜索?