如果没有TOP 10,使用TOP 10的查询如何比完全相同的查询完成更长的时间?

And*_*ena 9 sql sql-server full-text-search user-defined-functions sql-server-2008

我有一个非常简单的情况:

我有一个表值函数调用 FullTextPagina定义如下:

select * from Pagina as p where contains(p.PageText, @term)
Run Code Online (Sandbox Code Playgroud)

然后我有2个查询:

declare @term nvarchar(4000)= N'"DIEGO NUNES J COMBINADO"'

SELECT Id, DtPagina
FROM FullTextPagina(@term)
ORDER BY DtPagina DESC

SELECT TOP 10 Id, DtPagina
FROM FullTextPagina(@term)
ORDER BY DtPagina DESC
Run Code Online (Sandbox Code Playgroud)

它们是相同的,除了第二个包含TOP 10声明的事实.并且他们不会返回任何东西.0行.

第一个立即执行.秒需要1:20米才能完成.

为什么?

PS:

  1. 我已经正确设置了全文索引
  2. 我有一个非聚集的,非唯一的降序索引 DtPagina
  3. 执行计划在这里:http://i.imgur.com/77vJB.png

编辑

回应@MartinSmith,奇怪的是,表值函数的"执行次数"对于TOP 10案例是118万,对于另一案例是1

编辑2

执行计划XML http://tecnologia.novaprolink.com.br/Execution%20plan.xml

编辑3

添加选项(重新编译)或取消参数不会影响结果

SELECT Id, DtPagina
FROM FullTextPagina(N'"DIEGO NUNES J COMBINADO"')
ORDER BY DtPagina DESC

SELECT TOP 10 Id, DtPagina
FROM FullTextPagina(N'"DIEGO NUNES J COMBINADO"')
ORDER BY DtPagina DESC
OPTION (RECOMPILE)
Run Code Online (Sandbox Code Playgroud)

编辑4

完整代码 FullTextPagina

USE [RexConsumo_2011_11]
GO

/****** Object:  UserDefinedFunction [dbo].[FullTextPagina]    Script Date: 11/24/2011 11:43:09 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[FullTextPagina] (@term nvarchar(4000))
RETURNS TABLE 
AS
RETURN 
(
    select * from Pagina as p where contains(p.PageText, @term)
)

GO
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 5

您遇到的问题是因为SQL Server无法准确估计与谓词匹配的行数.

你的查询正在进行中SELECT TOP 10 Id, DtPagina ... ORDER BY DtPagina DESC.关于如何做到这一点,有几种选择

选项1

它可以DtPagina DESC按顺序扫描索引并查看每行是否与全文谓词匹配,然后在找到索引顺序的前10个时退出.

选项2

  1. 评估全文谓词
  2. 检索DtPagina所有匹配行的列值
  3. 对它们进行排序并获得前10名.

在计算第一个选项时,底部计划显示它必须扫描大约600行才能获得10个匹配并且能够退出.这是一个大规模的低估,因为事实上没有行匹配谓词,它需要为整个1,186,533行执行此操作.

当从顶层计划中计算第二个选项时,可以看出它假定有13846.2个匹配的行将从全文索引查询中返回并需要连接和排序.由于实际数字为零,这是一个很大的估计.

所以这些不正确的估计导致它不正确地支持第一个选项.

我不确定如何提高全文索引统计数据的准确性.也许尝试使用重写查询containstable

编辑:这有点像黑客,但可能会很好.如果你尝试怎么办?

declare @term nvarchar(4000)= N'"DIEGO NUNES J COMBINADO"'
declare @num int = 10

SELECT TOP (@num) Id, DtPagina
FROM FullTextPagina(@term)
ORDER BY DtPagina DESC
Run Code Online (Sandbox Code Playgroud)

然后它会假设TOP 100哪个可能足以让它选择其他更有效的计划.

  • @AndréPena - 因为你是通过`DtPagina`订购的,它认为按顺序扫描该索引会更便宜,看看每行是否与全文谓词匹配而不是评估全文谓词,找到(估计13,846实际为0)匹配行然后对它们进行排序并获得前10名.但这是基于完全不正确的假设,即它只需要执行600次,因此它大大低估了该选项的成本,并且还高估了其他选项的成本. (2认同)