我正在为现有论坛开发搜索存储过程。
我编写了以下使用标准SQL全文索引的代码,但是我确信有更好的方法可以做到这一点,并希望在正确的方向上找到一个点。
要提供一些有关它如何工作的信息,该页面有一个搜索文本框,点击后将搜索主题标题,帖子描述和帖子文本,并应首先返回标题匹配的结果,然后描述然后发布数据。
以下是我到目前为止所写的内容,但它并不优雅,也不像我想的那样快。举一个20K线程和80K帖子的性能示例,搜索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%的查询时间。我无论如何都无法看到这一点。
由于
加文
答案 0 :(得分:1)
我真的必须看一个解释计划才能知道缓慢的部分在哪里,因为我没有在你的代码中看到任何特别讨厌的东西。第一件事 - 确保所有索引都处于良好状态,正在使用它们,统计数据是最新的等等。
另一个想法是首先在线程标题上进行搜索,然后使用其中的结果来修剪线程描述和发布文本的搜索。同样,使用线程描述搜索的结果来修剪帖子文本搜索。
这里的基本想法是,如果您在主题标题中找到关键字,为什么还要费心搜索说明和帖子?我意识到这可能不起作用,这取决于你如何向用户呈现搜索结果,它可能没有太大的区别,但它是值得思考的。
答案 1 :(得分:0)
80k的记录并不是那么多。我建议不要将结果数据插入临时表,而只是插入ID,然后再加入该表。这将节省写入临时表,因为您可以存储10000个整数,而不是10000个完整帖子(其中您丢弃除了一页之外的所有帖子)。这也可以减少扫描帖子所花费的时间。
看起来你需要两个临时表,一个用于线程,一个用于帖子。你可以在最终的选择中将它们结合起来。