использование REPLACE в предложении WHERE для проверки орфографических перестановок - MS SQL - PullRequest
6 голосов
/ 06 июня 2011

У меня есть таблица типа:

| id | lastname | firstname |
|  1 | doe      | john      |
|  2 | oman     | donald    |
|  3 | o'neill  | james     |
|  4 | onackers | sharon    |

По сути, пользователи будут искать по первым буквам фамилии.

Я хочу иметь возможность возвращать результаты, которыесодержать и не содержать пунктуации из базы данных.Например, когда пользователь ищет: в

, я хочу вернуть оба: o'neill, onackers

Я хочу, чтобы кто-то былвозможность поиска «o, on, oneill, o neill и т. д.», чтобы получить o'neill.

Таким образом, лучший способ сделать это, кажется, взять значение столбца фамилии и выполнить его поиск в двух перестановках вГДЕ предложение с ИЛИ.Один, где любые специальные символы заменены на _ в SQL, и другой, где пропущены все не-буквенные символы (включая пробелы).

Я полагаю, я могу использовать подчеркивание в замене SQL, чтобы оставить один пробелдоступно.

У меня небольшие проблемы с предложением WHERE.Я бы предпочел сделать это с простой заменой, а не создавать функцию регулярного выражения, если это возможно.Если это не так, то я понимаю:

@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

Я уверен, что запасная часть должна быть на другой стороне от LIKE.Я испортил структуру, но мне нужна помощь.

Я использую MSSQL Server 2005.

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

ОБНОВЛЕНИЕ

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

  1. Создать функцию регулярного выражения, используя CLR (извините, если я говорю это неправильно, я новичок в этом)
  2. Создание дополнительных столбцов в таблице или создание новой таблицы «fuzzyTable» с вычищенными фамилиями.

База данных обновляется один раз за ночь.Я фактически уже начал новый настольный подход, поскольку это было то, что я первоначально собирался сделать.Однако я начинаю думать, что разумнее добавлять «нечеткие» столбцы в основную таблицу, а затем при ночном обновлении добавлять скорректированные фамилии в новые / обновленные строки.

Переполнение стека: какой подходлучше?Определяемая пользователем функция REGEX, которую я могу использовать в SQL, и таким образом избежать лишних столбцов?Или добавить дополнительный столбец или два к таблице?Или новый стол?

Ответы [ 5 ]

3 голосов
/ 06 июня 2011

В зависимости от того, насколько сложным может стать ваш сценарий, это будет много работы, и тоже медленно.Но есть более гибкий подход.Рассмотрим что-то вроде этого, называемое initialTable:

| id | lastname | firstname |
|  1 | o'malley | josé      |
|  2 | omállèy  | dònáld    |
|  3 | o'neill  | jámès     |
|  4 | onackers | sharon    |

Может быть, немного, но это иллюстрирует общую проблему.Мне пришлось реализовать «нечеткий» поиск для нашего веб-сайта интрасети, основанный на символьных данных, которые выглядели очень похожими - например, во французских или испанских названиях или улицах много акцентов.

Я определил функцию, котораявыполнил все замены для данной строки, например (псевдокод):

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è.Это не может быть найдено ни в одной таблице.То, что вам нужно сделать, это:

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 (или функции замены).В нашем текущем сценарии использования база данных интрасети обновляется один раз в ночь, поэтому это не проблема.

EDIT

Вы должны знать, что, используя это,в некоторых случаях вы получите ложные срабатывания.Например, мы используем это для поиска сотрудников, и если у вас есть голландское имя, написанное Hoek, вы также найдете это имя в поиске Hök, потому что в немецком языке замена для ö будетбыть oe.Эту проблему можно решить с помощью функций замены, учитывающих особенности страны, но мы никогда не заходили так далеко.В зависимости от ваших входных данных это более или менее академично, для нашего случая использования я не могу вспомнить, чтобы кто-то жаловался.

Основная причина, по которой мы в первую очередь придумали этот подход, заключалась в том, что некоторые данныес которым нам приходилось работать было пронизано орфографическими ошибками, т.е.на французском многие гласные были акцентированы неправильно, но все же нам нужно было добиться результата.

3 голосов
/ 06 июня 2011

Я полагаю, что проблема, с которой вы столкнулись, заключается в том, что функция repalce в SQL-Server не принимает [^A-Za-z] для обозначения символов, отличных от alpa. Вместо этого он действительно ищет эту точную строку, чтобы заменить ее.

http://msdn.microsoft.com/en-us/library/ms186862%28v=sql.90%29.aspx


Что касается использования регулярных выражений, я сделал это только с помощью CLR, который, похоже, слишком увлекается этой конкретной проблемой.


Мой совет: держать поля поиска в двух разных форматах в самой таблице. А затем воспользуйтесь простым поиском LIKE.

WHERE last_name LIKE @last_name OR last_name_stripped LIKE @last_name

last_name_stripped может быть вычисляемым столбцом (возможно, с использованием функции для удаления всех символов, не являющихся альфа) или обрабатываться вашим клиентом во время вставки.

2 голосов
/ 06 июня 2011

Использование:

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 ).

2 голосов
/ 06 июня 2011

Если вам нужно выполнить относительно сложные поиски для столбца в большой таблице, может быть более эффективно создать второй столбец, содержащий данные, отформатированные для эффективных поисков (с немедленным предупреждением, что «подобные» поиски редко бывают эффективными ). Поэтому, если у вас есть столбец LastName, добавьте новый столбец, например LastNameLookup, и заполните этот столбец данными, отформатированными в соответствии с вашими критериями поиска. Если правила форматирования относительно просты, вы можете реализовать это как вычисляемый столбец столбца; если важна производительность, сделайте ее постоянным вычисляемым столбцом.

Также стоит упомянуть, что SQL не поддерживает регулярные выражения (хотя есть ограниченная форма, связанная с предложением LIKE в SQL 2008).

1 голос
/ 06 июня 2011

Хмм ... на примере классического жереха.Я предполагаю, что это из формы.Для этого примера я называю ваше текстовое поле «namesearch».Так что на странице, где вы запрашиваете .form ("namesearch"), просто присвойте strSearch = request.form ("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

...