Взвешенный полнотекстовый поиск SQL Server - PullRequest
7 голосов
/ 22 ноября 2008

В настоящее время у меня есть таблица, которую я ищу по 4 полям: FirstName, LastName, MiddleName и AKA. В настоящее время у меня есть CONTAINSTABLE поиск строк, и это работает. Не хорошо, но это работает. Теперь Я хочу, чтобы Имя было взвешено выше, а отчество - ниже.

Я нашел команду ISABOUT , но это кажется довольно бесполезным, если я должен сделать это по слову, а не по столбцу (надеюсь, я понял это неправильно). Это не вариант, если это слово, потому что я не знаю, сколько слов пользователь введет.

Я нашел нить здесь , в которой говорится об этом же решении, однако я не смог заставить работать принятое решение. Может быть, я сделал что-то не так, но независимо от того, что я не могу заставить это работать, и его логика кажется действительно ... странной. Должен быть более легкий путь.

Ответы [ 5 ]

7 голосов
/ 27 ноября 2008

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

Ключ в том, чтобы создать видимость изменения рейтинга, а не изменить рейтинг сервера SQL.

Пример использования табличной переменной:

DECLARE @Results TABLE (PersonId Int, Rank Int, Source Int)

Для таблицы Люди с столбцами PersonId Int PK Identity, FirstName VarChar(100), MiddleName VarChar(100), LastName VarChar(100), AlsoKnown VarChar(100) с каждым столбцом, добавленным в полнотекстовый каталог, можно использовать запрос:

INSERT INTO @Results (PersonId, Rank, Source)

SELECT PersonId, Rank, 1
FROM ContainsTable(People, FirstName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId

UNION
SELECT PersonId, Rank, 2
FROM ContainsTable(People, MiddleName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId

UNION
SELECT PersonId, Rank, 3
FROM ContainsTable(People, LastName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId

UNION
SELECT PersonId, Rank, 4
FROM ContainsTable(People, AlsoKnown, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId

/*
Now that the results from above are in the @Results table, you can manipulate the
rankings in one of several ways, the simplest is to pull the results ordered first by Source then by Rank.  Of course you would probably join to the People table to pull the name fields.
*/

SELECT PersonId
FROM @Results
ORDER BY Source, Rank DESC

/*
A more complex manipulation would use a statement to multiply the ranking by a value above 1 (to increase rank) or less than 1 (to lower rank), then return results based on the new rank.  This provides more fine tuning, since I could make first name 10% higher and middle name 15% lower and leave last name and also known the original value.
*/

SELECT PersonId, CASE Source WHEN 1 THEN Rank * 1.1 WHEN 2 THEN Rank * .9 ELSE Rank END AS NewRank FROM @Results
ORDER BY NewRank DESC

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

3 голосов
/ 30 ноября 2008

Ранги бесполезны по индексам, вы не можете объединить их и ожидать, что результат будет что-то значить. Ранговыми числами каждого индекса являются сравнения яблок / апельсинов / винограда / арбузов / пар, которые не имеют относительного значения WRT других индексов.

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

На мой взгляд, лучшее решение - поместить все данные, которые вы хотите найти для поиска, в один индекс / столбец FTS и использовать этот столбец для ранжирования, чтобы упорядочить вывод. Даже если вам нужно дублировать содержимое поля для достижения результата.

2 голосов
/ 08 ноября 2010

Всего несколько недель назад я решал очень похожую проблему, и решение оказалось удивительно простым (хотя и уродливым и занимающим много места). Создайте другой столбец, содержащий объединенные значения FirstName + FirstName + LastName + MiddleName в этом порядке. Повторяющийся столбец FirstName - , а не опечатка , это хитрость, чтобы заставить FT увеличивать значения веса из FirstName выше во время поиска

0 голосов
/ 05 июня 2014

Как насчет этого:

    SELECT p.* from Person p
left join ContainsTable(Person, FirstName, @SearchValue) firstnamefilter on firstnamefiler.key = p.id
left join ContainsTable(Person, MiddleName, @SearchValue) middlenamefilter on middlenamefilter.key = p.id
where (firstnamefilter.rank is not null or middlenamefilter.rank is not null)
order by firstnamefilter.rank desc, middlenamefilter.rank desc

Это создаст запись для каждой записи Person, в которой либо имя, либо второе имя (или оба) совпадают в поисковом запросе, а также порядок всех совпадений с первым именем (в порядке убывания), за которым следует все совпадения со вторым именем (снова в порядке убывания)

0 голосов
/ 04 декабря 2008

Я предполагаю, что возвращенные данные объединены с другими таблицами в вашей схеме? Я бы разработал ваш собственный RANK на основе столбцов от связанных данных до полнотекстового индекса. Это также обеспечивает гарантированный уровень точности значения RANK.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...