我有一个 Oracle 数据库,与许多数据库一样,它有一个包含传记信息的表。对此,我想以“自然”的方式按名称搜索。
该表有forename
和 surname
领域,目前,我正在使用这样的东西:
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
会太慢并且可能会干扰我无法控制的前端软件与数据库的交互方式;所以需要一些思考... score
功能。例如,我的开发数据显示“Jonathan Peter Jason Smith”在顶部——很好——但“Jane Margaret Simpson”与“John Terrance Smith”处于同一级别我认为删除前面的通配符可能会在不降低结果的情况下提高性能,因为在现实生活中,您永远不会在名称中间搜索块。但是,否则,我愿意接受想法......这个场景一定是令人作呕的!任何人都可以对我现在正在做/正在考虑的事情提出更好的方法吗?
谢谢 :)
最佳答案
按照评论中的建议,我提出了一个非常有效的解决方案。特别是@X-Zero 关于创建 Soundexes 表的建议:就我而言,我可以创建新表,但不允许更改现有架构!
所以,我的流程如下:
ID
、 token
、 sound
和 position
;主键( ID
、 sound
、 position
)和附加索引( ID
、 sound
)。 us7ascii
,因此重音字符被标准化。这是因为 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
连同他们的身份证。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 行...我对它非常满意,并希望将其投入生产。唯一的问题是:
mypeople
上执行相应的操作时,都需要在 mypeople
表上放置触发器以将标记插入/更新/删除到搜索表中。这可能会减慢系统速度;但由于这应该只发生在一年中的几个时期,也许更好的解决方案是按计划重建搜索表。 无论如何,希望有帮助:) 欢迎评论!
EDIT 我的完整解决方案(编写和实现)现在是 here ,使用 Metaphone 和 Damerau-Levenshtein 距离。
关于sql - 姓名查询数据库建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7612822/