Вопрос

Я работаю над хранимой процедурой поиска для наших существующих форумов.

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

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

Ниже приведено то, что я написал на данный момент, что работает, но не элегантно и не так быстро, как хотелось бы.Чтобы привести пример производительности с 20 тысячами тем и 80 тысячами сообщений, поиск 5 случайных слов занимает около 12 секунд.

ALTER PROCEDURE [dbo].[SearchForums]
(
    --Input Params
    @SearchText VARCHAR(200),
    @GroupId INT = -1,
    @ClientId INT,
    --Paging Params
    @CurrentPage INT,
    @PageSize INT,           
    @OutTotalRecCount INT OUTPUT
)
AS

--Create Temp Table to Store Query Data
CREATE TABLE #SearchResults
(
    Relevance INT IDENTITY,
    ThreadID INT,
    PostID INT,
    [Description] VARCHAR(2000),
    Author BIGINT
)

--Create and populate table of all GroupID's This search will return from
CREATE TABLE #GroupsToSearch
(
GroupId INT
)
IF @GroupId = -1
    BEGIN
        INSERT INTO #GroupsToSearch
        SELECT GroupID FROM SNetwork_Groups WHERE ClientId = @ClientId
    END
ELSE
    BEGIN
        INSERT INTO #GroupsToSearch
        VALUES(@GroupId)
    END

--Get Thread Titles
INSERT INTO #SearchResults
    SELECT 
        SNetwork_Threads.[ThreadId],
        (SELECT NULL) AS PostId,
        SNetwork_Threads.[Description],
        SNetwork_Threads.[OwnerUserId]
    FROM 
        SNetwork_Threads
        INNER JOIN SNetwork_Groups ON SNetwork_Groups.GroupId = SNetwork_Threads.GroupId        
    WHERE 
        FREETEXT(SNetwork_Threads.[Description], @SearchText) AND
        Snetwork_Threads.GroupID IN (SELECT GroupID FROM #GroupsToSearch) AND
        SNetwork_Groups.ClientId = @ClientId


--Get Thread Descriptions
INSERT INTO #SearchResults
    SELECT 
        SNetwork_Threads.[ThreadId],
        (SELECT NULL) AS PostId,
        SNetwork_Threads.[Description],
        SNetwork_Threads.[OwnerUserId]
    FROM 
        SNetwork_Threads
        INNER JOIN SNetwork_Groups ON SNetwork_Groups.GroupId = SNetwork_Threads.GroupId        
    WHERE 
        FREETEXT(SNetwork_Threads.[Name], @SearchText) AND
        Snetwork_Threads.GroupID IN (SELECT GroupID FROM #GroupsToSearch) AND
        SNetwork_Groups.ClientId = @ClientId


--Get Posts
INSERT INTO #SearchResults
    SELECT 
        SNetwork_Threads.ThreadId,
        SNetwork_Posts.PostId,
        SNetwork_Posts.PostText,
        SNetwork_Posts.[OwnerUserId]
    FROM 
        SNetwork_Posts 
        INNER JOIN SNetwork_Threads ON SNetwork_Threads.ThreadId = SNetwork_Posts.ThreadId
        INNER JOIN SNetwork_Groups ON SNetwork_Groups.GroupId = SNetwork_Threads.GroupId        
    WHERE 
        FREETEXT(SNetwork_Posts.PostText, @SearchText) AND
        Snetwork_Threads.GroupID IN (SELECT GroupID FROM #GroupsToSearch) AND
        SNetwork_Groups.ClientId = @ClientId


--Return Paged Result Sets
SELECT @OutTotalRecCount =  COUNT(*) FROM #SearchResults
SELECT  
    #SearchResults.[ThreadID],
    #SearchResults.[PostID],
    #SearchResults.[Description],
    #SearchResults.[Author]
FROM  
    #SearchResults          
WHERE  
    #SearchResults.[Relevance] >= (@CurrentPage - 1) * @PageSize + 1 AND 
    #SearchResults.[Relevance] <= @CurrentPage*@PageSize
ORDER BY Relevance ASC


--Clean Up
DROP TABLE #SearchResults
DROP TABLE #GroupsToSearch

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

В случае, если это помогает, 80% времени запроса занимает поиск сообщений и, согласно плану запроса, тратится на «сканирование кластерного индекса» в таблице сообщений.Я все равно ничего не вижу вокруг этого.

Спасибо

Гэвин

Это было полезно?

Решение

Мне действительно нужно было бы увидеть план объяснения, чтобы знать, где были медленные части, поскольку я не вижу в вашем коде ничего особенно неприятного.Самое первое — убедитесь, что все ваши индексы в порядке, используются, статистика актуальна и т. д.

Еще одна идея — сначала выполнить поиск по заголовку темы, а затем использовать результаты для сокращения поиска по описанию темы и тексту сообщения.Аналогичным образом используйте результаты поиска по описанию темы, чтобы сократить поиск по тексту сообщений.

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

Другие советы

80 тысяч записей — это не так уж и много.Я бы рекомендовал не вставлять полученные данные во временную таблицу, а вместо этого вставлять только идентификаторы, а затем присоединяться к этой таблице.Это позволит сэкономить на записи во временную таблицу, поскольку вы можете хранить 10 000 целых чисел вместо 10 000 полных сообщений (из которых вы отбрасываете все, кроме одной страницы).Это также может сократить время, затрачиваемое на сканирование сообщений.

Похоже, вам понадобятся две временные таблицы: одна для тем и одна для сообщений.Вы бы объединили их при окончательном выборе.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top