我正在查询一个包含用户搜索结果的全文索引的表,然后我将通配符搜索的结果附加到该结尾。
这是我稍微简化的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?
答案 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
,如果它不够高则只触发第二个