Полнотекстовый поиск SQL Server - создание одного вычисляемого столбца - PullRequest
1 голос
/ 14 января 2010

В настоящее время я работаю над проектом, в котором я хочу искать сотрудников с помощью всего одного входного поискового запроса. Для этого я использую SQL FTS.

Схема таблицы выглядит следующим образом

Таблица сотрудников

EmployeeId, Имя, Фамилия

Пример данных

1, Джон, Миллер

2, Чак, Норрис


Таблица адресов

AddressId, EmployeeId, CityId, Street, StreetNumber

Пример данных

1, 1, 1, проспект, 12

2, 2, 2, Wimbledon Rd, 12


Городской стол

CityId, Имя, ZipCode

Пример данных

1, Гамбург, 22335

2, Лондон, 12345


Так что теперь я получил следующий поисковый запрос:

  • Джон Гамбург: Означает Джона И Гамбурга и должен вернуть 1 запись.
  • Джон Лондон: означает Джон И Лондон и должен вернуть 0 записей, поскольку в Лондоне нет Джона.
  • Норрис Уимблдон: означает Норрис И Уимблдон и должен вернуть 1 запись.

Теперь проблема в том, что использование CONTAINSTABLE позволяет искать только одну таблицу за раз. Таким образом, применение «John AND Hamburg» к полному текстовому каталогу сотрудников возвращает 0 записей, поскольку «Hamburg» находится в таблице адресов.

Так что в настоящее время я могу использовать «ИЛИ» вместо «И», например:

SELECT
   (keyTblSp.RANK * 3) AS [Rank],
    sp.*
FROM Employee sp    
    INNER JOIN 
        CONTAINSTABLE(Employee, *, 'John OR Hamburg', 1000) AS keyTblSp
        ON sp.EmployeeId = keyTblSp.[KEY]    

UNION ALL
SELECT
   (keyTbl.RANK * 2) AS [Rank],
    sp.*
FROM Employee sp    
    LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId 
    INNER JOIN 
        CONTAINSTABLE([Address], *, 'John OR Hamburg', 1000) AS keyTbl
        ON addr.AddressId = keyTbl.[KEY]    
UNION ALL
SELECT
   (keyTbl.RANK * 2) AS [Rank],
    sp.*
FROM Employee sp    
    LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId 
    LEFT OUTER JOIN [City] cty ON cty.CityId = addr.CityId
    INNER JOIN 
        CONTAINSTABLE([City], *, 'John OR Hamburg', 1000) AS keyTbl
        ON cty.CityId = keyTbl.[KEY]  

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

Таблица сотрудников

EmployeeId, Имя, Фамилия, FulltextColumn

Пример данных

1 | Джон | Миллер | Проспект Джона Миллера, 12 Гамбург 22335

Так что тогда я мог бы сделать

SELECT
   (keyTbl.RANK) AS [Rank],
    sp.*
FROM Employee sp    
    INNER JOIN 
        CONTAINSTABLE([Employee], FulltextColumn, 'John AND Hamburg', 1000) AS keyTbl
        ON sp.EmployeeId = keyTbl.[KEY] 

Возможно ли это? Есть другие идеи?

Ответы [ 3 ]

1 голос
/ 17 января 2010

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

SELECT
   (keyTblSp.RANK * 3) AS [Rank],
    sp.*
FROM Employee sp    
    INNER JOIN 
        CONTAINSTABLE(Employee, *, 'John OR Hamburg', 1000) AS keyTblSp
        ON sp.EmployeeId = keyTblSp.[KEY]    
join
(
    SELECT
       (keyTbl.RANK * 2) AS [Rank],
        sp.*
    FROM Employee sp    
    LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId 
    INNER JOIN 
        CONTAINSTABLE([Address], *, 'John OR Hamburg', 1000) AS keyTbl
        ON addr.AddressId = keyTbl.[KEY]
UNION ALL
    SELECT
       (keyTbl.RANK * 2) AS [Rank],
        sp.*
    FROM Employee sp    
    LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId 
    LEFT OUTER JOIN [City] cty ON cty.CityId = addr.CityId
    INNER JOIN 
        CONTAINSTABLE([City], *, 'John OR Hamburg', 1000) AS keyTbl
        ON cty.CityId = keyTbl.[KEY]  
) addr_matches
on addr_matches.EmployeeId = sp.EmployeeId

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

0 голосов
/ 23 января 2010

Я думаю, что вы должны создать и индексировать представление и объединить все столбцы, которые можно использовать в FullText, чтобы объединить их в один столбец, разделяя их пробелами или тире, так как оба являются шумовыми словами для SQL Server 2005. Затемэто индексированное представление создает полнотекстовый индекс.

Содержит таблицу, по умолчанию не применяет FormsOf Inflectional или Forms of Thesaurus.Эти два варианта хороши для настройки и использования.

Если вы хотите перейти только на «ИЛИ», используйте FreeTextTable, как если бы по умолчанию применялись обе формы «Тезаурус» и «ФормыOf».

0 голосов
/ 19 января 2010

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

Чтобы создать вычисляемый столбец на основе данных в другой таблице, вам нужно будет создать его, используя UDF (пользовательскую функцию), например:

CREATE FUNCTION dbo.udf_ComputedColumnFunction (
    @EmployeeId INT
)
RETURNS VARCHAR(1000)
AS
BEGIN
    DECLARE @RET VARCHAR(1000)

    SELECT
        @RET = e.FirstName + ' ' + e.LastName + ' ' + a.Street  + ' ' +  a.StreetNumber + ' ' + c.Name + ' ' + c.ZipCode
    FROM Employee e
    INNER JOIN Address a ON a.EmployeeId = e.EmployeeId
    INNER JOIN City c ON c.CityId = a.CityId

    RETURN @RET
END
GO


ALTER TABLE Employee
ADD SearchColumn AS dbo.udf_ComputedColumnFunction(EmployeeId)

Если вы не хотите этого делать, вы можете:

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