SQL Server 2005フルテキストフォーラムの検索
-
03-07-2019 - |
質問
既存のフォーラムの検索ストアドプロシージャに取り組んでいます。
標準のSQLフルテキストインデックスを使用する次のコードを記述しましたが、より良い方法があると確信しており、正しい方向にポイントが必要です。
動作に必要な情報を提供するために、ページには1つの検索テキストボックスがあり、クリックするとスレッドのタイトル、スレッドの説明、投稿テキストが検索され、最初にタイトルが一致する結果が返され、次に説明、次にデータが投稿されます。
以下はこれまでに書いたものですが、動作しますが、エレガントではなく、私が望むほど速くありません。 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%が使用され、クエリプランに従って「クラスター化インデックススキャン」に費やされる場合投稿テーブルに。とにかくこの辺りは見えません。
ありがとう
ギャビン
解決
コードには特に厄介なものは何もないので、遅い部分がどこにあるかを知るために、本当に説明計画を見る必要があります。まず最初に-すべてのインデックスが正常に機能していること、使用されていること、統計が最新であることなどを確認してください。
もう1つのアイデアは、最初にスレッドタイトルで検索を実行し、その結果を使用して、スレッドの説明で検索を削除し、テキストを投稿することです。同様に、スレッド説明検索の結果を使用して、投稿テキスト検索を削除します。
ここでの基本的な考え方は、スレッドのタイトルにキーワードが見つかったら、なぜ説明と投稿をわざわざ検索するのかということです。検索結果をユーザーにどのように表示するかによっては、これが機能しない可能性があり、大きな違いはないかもしれませんが、考えてみてください。
他のヒント
80kレコードはそれほど多くありません。結果のデータを一時テーブルに挿入せず、IDのみを挿入してから、そのテーブルに結合することをお勧めします。これにより、10000の完全な投稿(1ページを除くすべての投稿)の代わりに10000のintを保存できるため、一時テーブルへの書き込みが節約されます。これにより、投稿のスキャンにかかる時間も短縮できます。
スレッド用と投稿用の2つの一時テーブルが必要になるようです。最終選択でそれらを結合します。