如何使用TOP并有条件地忽略UNION ALL?

时间:2012-06-29 13:58:11

标签: sql-server-2008 tsql optimization

我正在查询一个包含用户搜索结果的全文索引的表,然后我将通配符搜索的结果附加到该结尾。

这是我稍微简化的SQL ......

SELECT TOP(@top) * FROM     
(       
    SELECT TOP (@top) ft.[RANK], p.ProductID, p.Name FROM dbo.Product p 
    INNER JOIN FREETEXTTABLE(dbo.Product, *, @search_term) AS ft ON p.ProductID = ft.[KEY]
    ORDER BY ft.[RANK] * p.Popularity DESC

    UNION ALL

    SELECT TOP (@top) 0 AS [RANK], p.ProductID, p.Name FROM dbo.Product p 
    WHERE (NOT p.ProductID IN (SELECT [KEY] FROM FREETEXTTABLE(dbo.Product, *, @search_term)))
    AND   (p.Name LIKE '%' + @search_term + '%')
    ORDER BY p.Popularity DESC
) AS results

这一切都有效并且已经存在了一段时间。

现在这里是棘手的部分。我最近发现这是网站上更昂贵的查询之一。我查看了查询计划,发现查询的LIKE'%%'部分会产生50-80%的成本。这真的不是一个大惊喜,因为通配符搜索往往很慢。麻烦的是,当前半部分已经返回足够的行时,UNION ALL的后半部分不需要运行。

当前半部分(全文搜索)返回我需要的所有行时,有没有办法执行UNION ALL?

2 个答案:

答案 0 :(得分:3)

你可以这样做:

SELECT TOP (@top) ..., ft.[RANK] * p.Popularity INTO #foo ... <FT query>;

IF @@ROWCOUNT < @top
  INSERT #foo SELECT TOP (@top) ..., p.Popularity FROM <like query>;

SELECT TOP (@top) ... FROM #foo ORDER BY Popularity DESC;

这是一个更多的I / O,但可能值得抵消。

您也可以考虑将第一个查询返回给客户端,并仅在第一个查询不足的情况下返回第二个结果集,并使应用程序足够智能化以合并结果集。只有当意图在之前显示ft。[RANK] * p.Popularity 时,这才有效。仅仅由p.Popularity排名的任何结果。

答案 1 :(得分:0)

我不知道你可以在一个语句中做到这一点,但是你可以在第一个查询之后检查@@rowcount,如果它不够高则只触发第二个