#c# #tsql
#c# #tsql
Вопрос:
У меня есть запрос, который в настоящее время принимает единственную строку поиска, предоставленную пользователем, и пытается выполнить поиск по таблице, которая содержит отдельный столбец для FirstName и LastName. Если в строке поиска есть пробел, выполняемый запрос по существу выглядит следующим образом:
SELECT * FROM table
WHERE table.firstName LIKE @firstName '%'
AND table.lastName LIKE @lastName '%'
Нам не нужно беспокоиться о случае отсутствия пробелов в строке поиска.
Общий случай довольно прост — разделите строку поиска на пробел, первая часть — это имя, вторая часть — фамилия. Таким образом, «Боб Смит» становится
@firstName = "Bob", @lastName = "Smith"
На чем я зациклился, так это на том, как обрабатывать случаи, когда имя состоит более чем из двух слов. Ситуации, подобные
table.firstName table.lastName
--------------- --------------
Bob van Smith
Billy Bob Smith
Bob van der Smith
Billy Bob van der Smith
И так далее. Прямо сейчас мы разделяем первый пробел, поэтому первый пример «Bob van Smith» будет работать, потому что он разбивается на
@firstName: "Bob", @lastName: "van Smith"
Но это не улавливает второй случай «Билли Боб Смит», поскольку он разбивается на
@firstName: "Billy", @lastName: "Bob Smith"
Текущая настройка также будет работать в третьем тестовом примере, поскольку она разбивается на
@firstName: "Bob", @lastName: "van der Smith"
В последнем случае были бы бонусные баллы, если бы существовал способ заставить это работать.
Моей первой идеей было просто изменить запрос на
SELECT * FROM table
WHERE table.firstName ' ' table.lastName LIKE '%' @searchString '%'
Но это было отменено, потому что мы не хотим, чтобы кто-то искал только букву «a», например, чтобы вернуть тонны записей, которые создал бы двойной подстановочный знак.
Есть ли какие-либо хитрости для выполнения такого рода разделения строк / поиска? Это не может быть первым случаем, когда возникает проблема, но при поиске в Интернете я не смог найти ничего, кроме «разделить на пробел, но обратите внимание, что это не сработает, если в имени 3 или более слов».
Мне хочется чего-то вроде включения «между именами» как части @FirstName и @LastName и сделать что-то умное, или сделать часть SQL более универсальной, а затем выполнить дополнительную фильтрацию с помощью LINQ в моем коде C #, но решение ускользает от меня.
Комментарии:
1. Это нетривиальная проблема. Возьмем, к примеру, такое имя, как Мария де лос-Анджелес Гомес де ла Крус. Я думаю, что ваш дизайн поставил вас в тупик, из которого нелегко выбраться. Надежный алгоритм сопоставления имен должен учитывать возможность того, что люди могут по-разному относиться к одному и тому же имени, и допускать пропуск слов в имени, пересечение границ имени / фамилии, орфографические ошибки и т.д. Это слишком широко для простого ответа SO.
2. @hatchet Да, это то, чего я боялся. Я унаследовал этот дизайн и сопротивляюсь внесению в него фундаментальных изменений, поэтому я подумал, что не помешает посмотреть, не упустил ли я какой-нибудь трюк
Ответ №1:
Я сделал это с помощью:
- Использование подстановочного знака для сопоставления каждого имени, разделенного пробелом, с обоими столбцами (я не мог контролировать порядок имен или требовать имя и фамилию).
- Затем я присваиваю оценку на основе каждого совпадения (т. Е. Совпадало ли это целое слово и были ли совпадения в столбцах имени и фамилии)
- У меня также было минимальное количество букв для поиска (минимум 2 буквы)
У меня это хорошо сработало. Вы все еще можете сгенерировать большое количество совпадений для маленьких слов. Ранжирование совпадений приведет к тому, что плохие частичные совпадения окажутся в нижней части списка.
Комментарии:
1. Мне нравится эта идея, и я думаю, что это может быть полезно для кого-то еще, кто может найти этот вопрос в будущем. К сожалению, в моем случае требуется «совпадение» или «нет совпадения». Я подозревал, что у меня слишком много ограничений, чтобы это действительно было выполнимо, и, похоже, я был прав
2. Когда вы говорите «совпадает» или «не совпадает» — вы имеете в виду по всему имени? Если это так, сначала выполните поиск по
concat
вашимFirstname
/Lastname
столбцам (т. Е. Боб Смит не совпадает с Бобом ван Смитом, вы золотой). Если вы имеете в виду не поиск частичных совпадений слов, а гибкость в подборе не всех имен, вы также можете это сделать, разделив ваши поисковые слова и слова в вашихFirstName
/LastName
столбцах и оценив их по порядку.3. В итоге мы пришли к довольно сложной идее поддержания поиска по имени и фамилии при наличии одного пробела, но объединения и прямого сравнения, как вы предложили здесь, при наличии нескольких пробелов. Хотя наша ситуация была довольно уникальной, я думаю, что ваш ответ лучше подходит для общих целей, поэтому я отмечаю его принятым
Ответ №2:
Если вы используете sql server 2012 , вы можете использовать CONCAT (Transact-SQL).
select * from [table] where CONCAT(FirstName, ' ', LastName) like '%Bob Smith%'
В этом случае вам не нужно ничего разделять, просто ОБЪЕДИНИТЕ ваши имя и фамилию и сравните их с вашими входными данными. Если вы собираетесь всегда получать полное имя из входных данных, вы даже можете перейти:
select * from [table] where CONCAT(FirstName, ' ', LastName) ='Bob Smith'
Конечно, вы будете использовать параметры sql, я пишу это как обычный текст только для того, чтобы было понятно использовать ввод полного имени.
Комментарии:
1. Приведет ли это к сканированию таблицы для каждого запроса?
2. @hatchet вероятно, я не знаю, как он настроил свою таблицу, для таких проверок есть профилировщик.
3. Разве это не та идея, которая у меня была, которая была отвергнута?
4. @Adam Я тебя не понял. Наверняка это будет сделано быстрее, чем поиск по 2 столбцам с помощью like.
Ответ №3:
Если вы ищете только точное соответствие тому, что ввел пользователь, попробуйте это:
select *
from table
where @searchString like table.firstName '%' and @searchString like '%' table.lastName
and len(table.firstName ' ' table.lastName) = len(@searchString)