我们有一个存储过程,它根据一个不同场景的输入参数来搜索产品。根据输入参数,搜索涉及从两个到大约十二个不同的表。为了避免不必要的连接,我们将实际的搜索查询构建为动态SQL,并在存储过程中执行它。
在一个最基本的方案中,用户仅通过关键字搜索产品(请参阅下面的查询1 ),这通常只需不到一秒钟。但是,如果他们按关键字和部门(查询2 )进行搜索,则执行时间会超过一分钟,执行计划看起来会有所不同(计划的附加快照正在显示只是不同的部分。)
查询1(快速)
SELECT DISTINCT
Product.ProductID, Product.Title
FROM
Product
INNER JOIN ProductVariant ON (ProductVariant.ProductID = Product.ProductID)
WHERE (1=1)
AND (CONTAINS((Product.*), @Keywords) OR CONTAINS((ProductVariant.*), @Keywords))
AND (Product.SourceID = @SourceID)
AND (Product.ProductStatus = @ProductStatus)
AND (ProductVariant.ProductStatus = @ProductStatus)
查询2(慢)
SELECT DISTINCT
Product.ProductID, Product.Title
FROM
Product
INNER JOIN ProductVariant ON (ProductVariant.ProductID = Product.ProductID)
WHERE (1=1)
AND (CONTAINS((Product.*), @Keywords) OR CONTAINS((ProductVariant.*), @Keywords))
AND (Product.SourceID = @SourceID)
AND (Product.DepartmentID = @DepartmentID)
AND (Product.ProductStatus = @ProductStatus)
AND (ProductVariant.ProductStatus = @ProductStatus)
Product和ProductVariant表都有一些参与全文索引的字符串列。 Product表在SourceID列上有一个非聚集索引,另一个在SourceID + DepartmentID上编入索引的非聚簇(这种冗余不是疏忽,而是打算)。 ProductVariant.ProductID是产品的FK,并且在其上具有非聚集索引。将更新所有索引和列的统计信息,SQL Management Studio不会报告丢失的索引。
关于可能导致这种截然不同的表现的任何建议?
P.S。忘记提及Product.DepartmentID是一个部门表的FK,以防它有任何区别。
答案 0 :(得分:1)
感谢@MartinSmith建议将全文搜索逻辑分解为临时表,然后使用它们来过滤主查询的结果。以下仅在2秒内返回:
SELECT
[Key] AS ProductID
INTO
#matchingProducts
FROM
CONTAINSTABLE(Product, *, @Keywords)
SELECT
[Key] AS VariantID
INTO
#matchingVariants
FROM
CONTAINSTABLE(ProductVariant, *, @Keywords)
SELECT DISTINCT
Product.ProductID, Product.Title
FROM
Product
INNER JOIN ProductVariant ON (ProductVariant.ProductID = Product.ProductID)
LEFT OUTER JOIN #matchingProducts ON #matchingProducts.ProductID = Product.ProductID
LEFT OUTER JOIN #matchingVariants ON #matchingVariants.VariantID = ProductVariant.VariantID
WHERE (1=1)
AND (Product.SourceID = @SourceID)
AND (Product.ProductStatus = @ProductStatus)
AND (ProductVariant.ProductStatus = @ProductStatus)
AND (Product.DepartmentID = @DepartmentID)
AND (NOT #matchingProducts.ProductID IS NULL OR NOT #matchingVariants.VariantID IS NULL)
奇怪的是,当我尝试使用如下所示的嵌套查询来简化上述解决方案时,结果在速度方面介于两者之间(大约25秒)。从理论上讲,下面的查询应该与上面的查询相同,但不知何故,SQL Server在内部以不同的方式编译第二个查询。
SELECT DISTINCT
Product.ProductID, Product.Title
FROM
Product
INNER JOIN ProductVariant ON (ProductVariant.ProductID = Product.ProductID)
LEFT OUTER JOIN
(
SELECT
[Key] AS ProductID
FROM
CONTAINSTABLE(Product, *, @Keywords)
) MatchingProducts
ON MatchingProducts.ProductID = Product.ProductID
LEFT OUTER JOIN
(
SELECT
[Key] AS VariantID
FROM
CONTAINSTABLE(ProductVariant, *, @Keywords)
) MatchingVariants
ON MatchingVariants.VariantID = ProductVariant.VariantID
WHERE (1=1)
AND (Product.SourceID = @SourceID)
AND (Product.ProductStatus = @ProductStatus)
AND (ProductVariant.ProductStatus = @ProductStatus)
AND (Product.DepartmentID = @DepartmentID)
AND (NOT MatchingProducts.ProductID IS NULL OR NOT MatchingVariants.VariantID IS NULL)
答案 1 :(得分:-1)
这可能是你的错误
为了避免不必要的连接,我们将实际的搜索查询构建为动态SQL并在存储过程中执行它。
在大多数情况下,服务器无法优化动态SQL。有一些技术可以缓解这种情况。阅读更多The Curse and Blessings of Dynamic SQL
摆脱动态SQL并使用适当的索引构建一个体面的查询。我向你保证;在优化方面,SQL Server比你更了解。如果必须(或一百个),则定义十个不同的查询。
其次......为什么在运行不同的查询时,您会期望相同的执行计划?使用不同的列/索引?根据您的方法,您获得的执行计划和结果对我来说似乎很自然。
您不会获得相同的执行计划/性能,因为您没有在第一个查询中查询列'DepartmentID'。