所以这比我预期的要容易。在实现了一个相当简单的查询来处理这个问题之后,我立即获得了比我想象的要好得多的性能。所以我不确定是否有必要实施和测试其他解决方案。
我目前的数据库中有大约 200 个着色器和 500 个标签。我运行了一个我认为有点现实的测试,其中我对我的存储过程执行了 35 个不同的搜索查询,带有不同数量的标签,有和没有搜索词。我将所有这些都放在一条 SQL 语句中,然后在 ASP.NET 中对结果进行基准测试。它始终在 200 毫秒内运行这 35 次搜索。如果我将它减少到只有 5 次搜索,那么时间会下降到 10 毫秒。这样的表现太棒了。这有助于我的数据库大小很小。但我认为查询很好地利用索引也有帮助。
我在查询中更改的一件事是我查找标签的方式。我现在正在通过它们的 id 而不是名称来查找标签。通过这样做,我可以少做 1 次连接,并受益于使用索引进行搜索。然后我还添加了“dbo”。在了解到 SQL 以每个用户为基础缓存查询之后,放在表名的前面。
如果有人感兴趣,这是我完成的存储过程:
ALTER PROCEDURE [dbo].[search]
@search_term varchar(100) = NULL,
@tag1 int = NULL,
@tag2 int = NULL,
@tag3 int = NULL,
@tag4 int = NULL,
@tag5 int = NULL,
@tag6 int = NULL,
@tag7 int = NULL,
@tag8 int = NULL,
@tag9 int = NULL,
@tag10 int = NULL
AS
BEGIN
SET NOCOUNT ON;
IF LEN(@search_term) > 0
BEGIN
SELECT s.shader_id, s.page_name, s.name, s.description, s.download_count, s.rating, s.price FROM dbo.shader s
INNER JOIN FREETEXTTABLE(dbo.shader, *, @search_term) AS ft ON s.shader_id = ft.[KEY]
WHERE (@tag1 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag1))
AND (@tag2 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag2))
AND (@tag3 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag3))
AND (@tag4 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag4))
AND (@tag5 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag5))
AND (@tag6 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag6))
AND (@tag7 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag7))
AND (@tag8 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag8))
AND (@tag9 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag9))
AND (@tag10 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag10))
ORDER BY ft.[RANK] DESC
END
ELSE
BEGIN
SELECT s.shader_id, s.page_name, s.name, s.description, s.download_count, s.rating, s.price FROM dbo.shader s
WHERE (@tag1 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag1))
AND (@tag2 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag2))
AND (@tag3 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag3))
AND (@tag4 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag4))
AND (@tag5 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag5))
AND (@tag6 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag6))
AND (@tag7 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag7))
AND (@tag8 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag8))
AND (@tag9 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag9))
AND (@tag10 IS NULL OR EXISTS(SELECT 1 AS num FROM dbo.tag_shader_xref tsx WHERE tsx.shader_id = s.shader_id AND tsx.tag_id = @tag10))
END
END
尽管我没有用尽所有选项,但这仍然是一个很好的练习,因为我向自己证明了我的数据库设计非常适合这项任务。我也从发布这个问题中学到了很多东西。我知道 exec() 不好,因为它不缓存查询计划。但我不知道 sp_executesql 缓存查询计划,这很酷。我也不知道公用表表达式。并且 Henrik Opel 发布的链接充满了这类任务的好技巧。
当然,如果数据库急剧增长,我仍然可能在一年后重新审视这一点。在此之前,感谢大家的帮助。
更新:
因此,如果有人有兴趣看到它的实际效果,我在http://www.silverlightxap.com/controls上有一个在线搜索引擎的工作示例。