использование предложения 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.

большое спасибо заранее.

обновление

Кажется, у меня есть два варианта:

  1. создать регулярное выражение функция с использованием CLR (извините, если я говорю это неправильно, я новичок в этом)
  2. создайте дополнительные столбцы в таблице или создайте новую "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 работа.) Ключ здесь это изменить строку поиска ввода с помощью функции замены; таким образом, вы получите совпадение при поиске на содержание потому что оба сводятся к 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