Так что это было проще, чем я ожидал. После реализации довольно простого запроса, чтобы позаботиться об этом, у меня сразу же была намного лучшая производительность, чем я думал. Поэтому я не уверен, что необходимо внедрять и тестировать другие решения.
В настоящее время моя база данных заполнена примерно 200 шейдерами и 500 тегами. Я выполнил, как мне кажется, несколько реалистичный тест, где я выполнил 35 различных поисковых запросов для моего сохраненного процесса с различным количеством тегов, с поисковым запросом и без него. Я поместил все это в один оператор SQL, а затем сравнил результаты в ASP.NET. Он последовательно запускал эти 35 поисков менее чем за 200 миллисекунд. Если я уменьшу его до 5 запросов, то время сократится до 10 мс. Такое представление потрясающе. Помогает, что размер моей базы данных небольшой. Но я думаю, что также помогает то, что запрос хорошо использует индексы.
Одна вещь, которую я изменил в своем запросе, - это способ поиска тегов. Сейчас я ищу теги по их идентификатору, а не по имени. Делая это, я могу сойтись с выполнением на 1 соединение меньше, и у меня есть преимущество использования индекса для поиска. А потом я также добавил «дбо». перед именами таблиц, узнав, что 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 кеширует планы запросов, и это очень круто. Я также не знал об общих табличных выражениях. И ссылка, размещенная Хенриком Опелем, полна хороших советов для этого типа задач.
Конечно, я все еще могу вернуться к этому через год, если база данных резко возрастет. До тех пор, спасибо всем за помощь.
UPDATE:
Итак, у меня есть рабочий пример этой поисковой системы в Интернете по адресу http://www.silverlightxap.com/controls, если кому-то интересно увидеть это в действии.