Интеллектуальный поиск средствами MS SQL Server (2000)

Источник: habrahabr
imhot

Многие сталкивались с необходимостью реализации интеллектального поиска по базе данных (думаю, что почти все). Тем, кто не нашел подходящего решения или тем, кто просто хотел бы взглянуть на альтернативные решения проблемы, будет полезен следующий код.
Здесь я привожу свою реализацию хранимой процедуры поиска по БД. Возможно, она довольно узкая, т.к. я оптимизировал ее под конкретные задачи и логику. Требования к поисковому запросу таковы, что он может содержать любые символы: кирилицу, цифры, латиницу. спец. символы и т.п.. Итак, код в студию ...

Пусть есть следующая таблица

Create Table [SearchTable]
(
[ID] Int,
[Name] Varchar(350),
[Date] DateTime
)


* This source code was highlighted with Source Code Highlighter.

Напишем для нее процедуру поиска

CREATE PROCEDURE Search
  @Query As Varchar(500)
AS
BEGIN
  /*Будем бить запрос на токены. В данном случае, разделяя по пробелу*/
  Declare @SearchTokens Table
    (
      [Token] Varchar(500)
    );

  /*Временная табличка для результатов. Relevance - будет содержать вес результата*/
  Declare @ResTable Table
    (
      [ID] Int,
      [Name] Varchar(350),
      [Date] DateTime,      
      [Relevance] SmallInt
    );
  
  Declare @CheckStr As Varchar(510);  

  
  Declare @Occurrences As Int;
  Declare @Cnt As Int;
  Declare @tmpStr As Varchar(500);  

  /*Удаляем начальные пробелы*/
  Set @Query = LTrim(@Query);

  /*Концевой символ должен быть пробел*/
  If Substring(@Query, Len(@Query),1) <> ` `
  Begin
    Set @Query = @Query + ` `;
  End;

  /*Сводим все серии пробелов в строке до серий длиной в один символ*/
  While Charindex(` `, @Query) <> 0
  Begin
    Set @Query = Replace(@Query, ` `, ` `);
  End
  
  /*Сколько будет токенов?*/
  Set @Occurrences = Datalength(Replace(@Query, ` `, ` #`)) - Datalength(@Query);  
  Set @tmpStr = @Query;  
  Set @Cnt = 0  
  /*Бьем на токены и заполняем таблицы*/
  While @Cnt <= @Occurrences
  Begin
    Set @Cnt = @Cnt + 1
    /*Берем очередной токен*/
    Set @CheckStr = Substring(@tmpStr, 1, Charindex(` `, @tmpStr) - 1);
    /*Ничего не делаем, если такой уже был*/
    If (SELECT * FROM @SearchTokens WHERE [Token] = @CheckStr) Is Not Null
    Begin
      Continue;
    End
    /*добавляем к токенам*/
    INSERT INTO @SearchTokens VALUES ( @CheckStr );
    /*Небольшое "регулярное выражение". У меня - такое. Вы можете сюда поместить такое, какое необходимо*/
    Set @CheckStr = `%` + @CheckStr + `%`;    
    INSERT INTO @ResTable SELECT S.[ID], S.[Name], S.[Date] 0 FROM [SearchTable] S
            WHERE (S.[Name] LIKE @CheckStr OR
               YEAR ( S.[Date]) LIKE @CheckStr)
              AND NOT (SELECT 1 FROM @ResTable
                      WHERE [ID] = S.[ID]) ="#0000ff">FROM
@SearchTokens WHERE [Token] = @CheckStr) Is Null;
    /*Отрезаем то, что проанализировали*/
    Set @tmpStr = Substring(@tmpStr, Charindex(` `, @tmpStr) + 1, 500)
    If Datalength(@tmpStr) = 0
    Begin
      Break;
    End;
  End;
  
  /*Анализируем данные*/
  Declare Curs Cursor For SELECT * FROM @SearchTokens;
  Declare @Token As Varchar(500);
  Open Curs;
  Fetch Curs Into @Token;
  While @@FETCH_STATUS = 0
  Begin
    UPDATE @ResTable SET [Relevance] = [Relevance] + 1 WHERE [Name] LIKE `%` + @Token + `%`;
    UPDATE @ResTable SET [Relevance] = [Relevance] + 1 WHERE YEAR ( [Date]) LIKE `%` + @Token + `%`;
    Fetch Curs Into @Token;
  End;
  Close Curs;
  Deallocate Curs;
  /*Отдаем результаты упорядочивая по весу */
  SELECT [ID]  , [Name], [Date
    FROM @ResTable ORDER BY [Relevance] DESC;  
END

* This source code was highlighted with Source Code Highlighter.

Послесловие

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

Заранее приношу извинения за возможные ошибки в коде, т.к. я урезал имеющуюся процедуру для простоты примера.

UPD: Опять забыл уточнить. Данное решение разработано для MS SQL Server 2000. Для более поздних версий существуют другие, более оптималные решения.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=17803