использование предложения REPLACE in WHERE для проверки орфографических перестановок-MS SQL
у меня есть таблица, как:
| id | lastname | firstname |
| 1 | doe | john |
| 2 | oman | donald |
| 3 | o'neill | james |
| 4 | onackers | sharon |
по сути, пользователи будут искать по первым буквам фамилии.
Я хочу иметь возможность возвращать результаты, которые содержат и не содержат знаков препинания из базы данных. Например, когда пользователь ищет: on
Я хочу вернуть обе: о'Нил, onackers
Я хочу, чтобы кто-то мог искать "o, on, oneill, o neill и т. д.", Чтобы получить о'Нил.
таким образом, лучший способ сделать это, кажется, взять значение столбца lastname и иметь две его перестановки в предложении WHERE с OR. Один, где любые специальные символы заменяются на _ В SQL, и один, где все не альфа-символы (включая пробелы) исчезли.
Я полагаю, что могу использовать подчеркивание в замене SQL, чтобы сохранить одно свободное пространство.
у меня небольшие проблемы с предложением WHERE. Я бы предпочел сделать это с простая замена, а не создание функции regex, если это возможно. Если это не идти, хотя, я понимаю:
@last_name (this is the nvarchar input)
SELECT id, lastname, firstname
FROM people
WHERE ((REPLACE(people.lastname, '[^A-Za-z]', '_') like @last_name + '%')
OR (REPLACE(people.lastnname,'[^A-Za-z ]', '') like @last_name + '%'))
ORDER BY lastname
Я уверен, что заменяющая часть должна быть на другой стороне. Я разрушаю структуру, но мне нужна помощь.
Я использую MSSQL Server 2005.
большое спасибо заранее.
обновление
Кажется, у меня есть два варианта:
- создать регулярное выражение функция с использованием CLR (извините, если я говорю это неправильно, я новичок в этом)
- создайте дополнительные столбцы в таблице или создайте новую "fuzzyTable" с очищенными фамилиями.
база данных обновляется раз в сутки. На самом деле я уже начал новый подход к таблице, так как это было то, что я изначально собирался сделать. Тем не менее, я начинаю думать, что разумнее добавить" нечеткие " столбцы в основную таблицу, а затем в ночное обновление, чтобы добавить скорректированный последние имена новых / обновленных строк.
переполнение стека: какой подход лучше? Пользовательская функция REGEX, которую я могу использовать в SQL, и, таким образом, избежать дополнительных столбцов? Или добавить лишний столбец или два в таблицу? Или новый стол?
5 ответов
в зависимости от того, насколько сложным может стать ваш сценарий, это будет много работы и тоже медленно. Но есть более гибкий подход. Рассмотрим что-то вроде этого, называемое initialTable
:
| id | lastname | firstname |
| 1 | o'malley | josé |
| 2 | omállèy | dònáld |
| 3 | o'neill | jámès |
| 4 | onackers | sharon |
может быть, немного, но это иллюстрирует общую проблему. Мне пришлось реализовать "нечеткий" поиск нашего веб - сайта в интранете на основе символьных данных, которые выглядели очень похожими-например, есть много акцентов во французских или испанских именах или уличных адресах.
Что Я did определял функцию, которая выполняла все замены для данной строки, например (псевдокод):
function string replacestuff(string input)
{
input = replace(input, "è", "e");
input = replace(input, "é", "e");
input = replace(input, "ò", "o");
input = replace(input, "ó", "o");
input = replace(input, "'", "");
...
return input;
}
используя эту функцию преобразования, создайте вторую таблицу fuzzyTable
, который имеет следующее содержание:
| id | lastname | firstname |
| 1 | omalley | jose |
| 2 | omalley | donald |
| 3 | oneill | james |
| 4 | onackers | sharon |
теперь предположим, что вы получите входную строку для поиска josè
. Это не может быть найдено ни в одной таблице. Вам придется сделать вот что:--17-->
declare @input varchar(50)
declare @input_mod varchar(50)
set @input = 'josè'
set @input_mod = replacestuff(@input)
SELECT id FROM initialTable WHERE firstname like @input OR firstname like @input_mod
UNION
SELECT id FROM fuzzyTable WHERE firstname like @input OR firstname like @input_mod
GROUP BY id
(конечно, вы должны добавить %
сделать LIKE
работа.) Ключ здесь это изменить строку поиска ввода с помощью функции замены; таким образом, вы получите совпадение при поиске sè
на содержание sé
потому что оба сводятся к se
при обработке функцией замены.
вы даже можете выполнить двухуровневый поиск; сначала проверьте только немодифицированную строку против правильной таблицы, а затем с помощью инструкции, показанной выше, выполните нечеткий поиск, если пользователь так говорит.
это очень гибкий подход и может обрабатывать всевозможные вещи, например, находить немецкие буквы ä, ö, ü, ß, используя двухбуквенные выражения ae, oe, ue, ss. Недостатком является то, что вам придется хранить дубликаты некоторых данных и изменять эти дубликаты в fuzzyTable по мере изменения initialTable (или функции замены). В нашем текущем случае использования база данных интрасети обновляется один раз за ночь, так что это не проблема.
редактировать
вы должны знать, что, используя это, в некоторых случаи, когда вы получите ложные срабатывания. Например, мы используем это для поиска сотрудников, и если у вас есть голландское имя, написанное Hoek
, вы также найдете это имя в поиске Hök
, потому что в немецком языке замена для ö
будет oe
. Это можно было бы решить с помощью функций замены, учитывающих потребности стран, но мы никогда не заходили так далеко. В зависимости от ваших входных данных это более или менее академично, для нашего случая использования я не помню, чтобы кто-то жаловался.
в основная причина, по которой мы пришли к такому подходу в первую очередь, заключалась в том, что некоторые данные, с которыми нам приходилось работать, были пронизаны орфографическими ошибками, т. е. на французском многие гласные были ударением неправильно, но все же нам нужно было добиться результата.
Я считаю, что проблема в том, что функция repalce SQL-Server не принимает [^A-Za-z]
означает"символы, отличные от alpa". Вместо этого он фактически ищет эту точную строку, чтобы заменить ее.
http://msdn.microsoft.com/en-us/library/ms186862%28v=sql.90%29.aspx
что касается использования регулярных выражений, я сделал это только с помощью среды CLR, которая, похоже, становится слишком вовлеченной для этого конкретного проблема.
мой совет состоял бы в том, чтобы держать поля для поиска в двух разных форматах в самой таблице. А затем используйте простой поиск.
WHERE last_name LIKE @last_name OR last_name_stripped LIKE @last_name
last_name_stripped может быть вычисляемым столбцом (возможно, с помощью функции для удаления всех символов non_alpha) или обрабатывается вашим клиентом во время вставки.
Если вам нужно выполнить относительно сложный поиск столбца в большой таблице, было бы более эффективно создать второй столбец, содержащий данные, отформатированные для эффективного поиска (с немедленным предостережением, что "подобные" поиски редко эффективны). Итак, где у вас есть столбец LastName
добавить новый столбец типа LastNameLookup
и заполните этот столбец данными, отформатированными в соответствии с критериями поиска. Если правила форматирования относительно просты, вы можете реализовать это как столбец вычисляемого столбца; если важна производительность, сделайте его сохраненным вычисляемым столбцом.
кроме того, SQL не поддерживает регулярные выражения (хотя в SQL 2008 есть ограниченная форма, связанная с предложением LIKE).
использование:
WHERE ( REPLACE(people.lastname, '[^A-Za-z]', '') LIKE @last_name + '%' )
или
WHERE ( ComplexFunction( field ) LIKE whatever )
скорее всего, в результате ваш запрос не будет использовать индекс (если он есть) поля people.lastname
и, таким образом, сканировать всю таблицу при каждом запуске запроса.
Я вижу два способа избежать этого:
одно, добавить другое поле lastnameStripped
к столу, где ComplexFunction(lastname)
хранится и индекс для этого поля. Затем вы можете искать с либо:
WHERE ( lastnameStripped LIKE REPLACE(@last_name, '[^A-Za-z]', '') + '%' )
или
WHERE ( lastnameStripped LIKE @last_name + '%' )
и оба будут использовать индекс lastnameStripped
.
два создать индексированное представление С ComplexFunction( lastname )
в поле.
Мда...используя классический пример asp. Я предполагаю, что это из формы. В этом примере я называю ваше текстовое поле "namesearch". Итак, страница, на которой вы запрашиваете.форма ("namesearch"), просто назначьте strSearch = request.форма("namesearch"). Затем, прежде чем запускать его в SQL-запрос, сделайте что-то вроде этого:
strSearch = request.form("namesearch") 'to get textbox info from form
strSearch = replace(strSearch," ", "") 'to remove spaces
strSearch = replace(strSearch,"'", "") 'to remove apostrophes
для SQL
SELECT id, lastname, firstname FROM people WHERE people.lastname like '%"& strSearch &"%' ORDER BY lastname
протестировано и работает с использованием VBScript и SQL 2005 Server