Вот один из способов - игнорировать большинство тонкостей, которые я упомянул в своем комментарии к вашему вопросу. Единственный, к которому я обратился, - это поиск без учета регистра.
Входные данные, такие как 'John Frusciante', задаются как переменная связывания :i_name
. Имя может быть одним, двумя, тремя или любым другим числом «токенов» - и они могут присутствовать в любом порядке, включая бессмысленные порядки, такие как Hussein Obama Barack
(где Обама - фамилия, а Барак Хуссейн - данные имена имя и отчество в американской терминологии). Для теста я использовал 'John Frusciante' для переменной bind.
Регулярные выражения удобны, но не быстры. Запрос может быть выполнен быстрее различными способами (с использованием стандартных строковых функций, но также в Oracle 12.1 или выше с использованием предложения lateral
или cross apply
, et c.) Одной из проблем будет listagg()
, если ваша Oracle версия базы данных - 11.1 или ниже, поскольку эта функция была введена только в 11.2.
Стратегия проста - разложить каждое имя на его токены, а затем собрать их обратно в алфавитном порядке. Я предполагаю, что в таблице есть столбец id
(если нет, и если данные находятся в сохраненной таблице, я мог бы использовать rowid
, иначе я могу создать id
на лету, на дополнительном этапе).
with
table_1 (id, name) as (
select 1, 'John Frusciante' from dual union all
select 2, 'Gilmour David' from dual union all
select 3, 'Sinatra Frank' from dual union all
select 4, 'David Bowie' from dual union all
select 5, 'Frusciante John' from dual union all
select 6, 'Wilhelm Friedrich Nietzsche' from dual
)
, prep (id, name, ordered_name) as (
select id, name,
listagg(regexp_substr(name,'\S+', 1, level), ' ')
within group
(order by regexp_substr(name,'\S+', 1, level))
from table_1
connect by level <= regexp_count(name, '\S+')
and prior id = id
and prior sys_guid() is not null
group by id, name
)
select name
from prep
where lower(ordered_name) =
(select lower(listagg(regexp_substr(:i_name,'\S+', 1, level), ' ')
within group
(order by regexp_substr(:i_name,'\S+', 1, level)))
from dual
connect by level <= regexp_count(:i_name, '\S+')
)
;
Выход (для ввода 'John Frusciante'
):
NAME
---------------
John Frusciante
Frusciante John